Build a scalable architecture for personalized, in-game ads using Redpanda, Quarkus, and Streamlit

ByDunith DhanushkaonMay 2, 2023
Unlocking profit: monetize in-game ads and purchases in real time

Welcome to the fourth post in our series showcasing how to power up your real-time gaming architecture with Redpanda. We kicked off with a general overview of 5 popular data streaming use cases. Then we walked through how to build a real-time gaming leaderboard and how to run real-time game data analysis. Now, let’s take the game data analysis use case even further by adding in-game ads that appear exactly when players are most likely to engage with them.

As fun as game development may be, ultimately, gaming companies need to think about their bottom line. This mainly means keeping players engaged and strategically monetizing that engagement. Two popular strategies for monetization are to display third-party ads within the game, and offer in-game purchases for upgrades, downloadable content, and more.

No matter which strategy you choose, timing is everything.

For example, the best time to show players an ad for a paid weapon upgrade is when they’re about to enter battle—or have just lost one. Not while they’re exploring the landscape and have no need for weapons whatsoever. Showing players the right ad at the right time significantly increases the chance of engagement, which leads to increased revenue.

It may sound straightforward to serve perfectly-timed ads. But to successfully personalize a gaming experience in real time, you have to:

  • Collect player engagement events like clicks, wins, and losses

  • Analyze player engagement metrics to determine the right ad to display

  • Deliver the ad in the proper context

In this post, we’ll show you how to capture events from gaming frontends and ingest them to Redpanda. Redpanda will stream gaming telemetry to the Quarkus AdRecommender microservice, which returns relevant ads in real time. Lastly, the recommended ads are displayed to the gamers using Streamlit.

Before we dig into the code, we’ll briefly explain why Redpanda should be in your game development armory.

The challenges of real-time, in-game monetization

Apache Kafka® has been the standard choice for real-time data streaming for years. But with millions of players creating billions of events across several gaming platforms, your infrastructure and operations costs can skyrocket to unsustainable levels. When you run Kafka at scale, you also scale up the costs for Kafka brokers, additional components like Apache Zookeeper™, Schema Registry, Cruise Control, and storage for historical data.

Real-time gaming use cases call for cloud-native simplicity, maximized computing power, and a reliable, scalable architecture that won’t cost you a fortune to run and maintain. This is the vision we had when building the Redpanda streaming data platform. It’s API-compatible with Kafka without the operational need for a JVM or Zookeeper, so you can stick to the streaming apps and tools you already use.

Not only will Redpanda make developing in-game monetization easier, faster, and simpler—it’s also 10x faster and up to 6x more cost effective than Kafka. Redpanda deploys as a self-contained single binary, which means you only have to manage one distributed system– whether you’re deploying it on your own infrastructure or a fully-managed cloud service.

Now, let’s kick off the tutorial.

Build the architecture for real-time, in-game ad personalization

This example builds on our real-time game data analysis architecture, with the addition of the Quarkus AdRecommender microservice.

Diagram of a real-time, in-game ad architecture using Redpanda, Quarkus, and Streamlit.
Diagram of a real-time, in-game ad architecture using Redpanda, Quarkus, and Streamlit.

The gaming frontend captures and sends player engagement events into Redpanda. Redpanda then streams gaming telemetry to the Quarkus AdRecommender microservice, which returns personalized ads in real time. Then, we use the Streamlit framework to display those ads to the player.

You can find a reference implementation and the source code for this tutorial in the Redpanda GitHub repo, inside the 3-ads folder.

This real-time monetization solution is available as a Maven multi-module project, implemented with the Quarkus Java framework. It consists of two modules: frontend and recommender. Redpanda serves as the central event broker in the solution, which contains two topics: gaming.results and ads.

The GameCompleted events coming from gaming frontends are ingested into the gaming.results topic. Let’s assume these events are JSON-formatted and have the following structure:


The result attribute can be either WIN or LOSS. The ads topic contains the ads recommended by the recommender module, which has the following structure:

   “Title”:”ad title”,
   “Content”: “ad content”

That said, let’s set up our modules.

1. Set up the real-time personalization frontend

The frontend module mimics the game’s user interface (UI), which consists of a simple HTML page and a REST endpoint. This module produces GamingCompleted events to the gaming.results Redpanda topic, and subscribes to the ads topic to receive real-time ad recommendations.

The code for the HTML page is located inside the 3-ads folder. When the page loads, the following JavaScript code snippet establishes a server-sent events (SSE) connection with the REST endpoint to receive ads, and then displays them on the UI.

var source = new EventSource(/ads”);
	source.onmessage = (event) => {
  	var ad = JSON.parse(;
  	$(“#ad”).html(<h3>+ ad.title +</h3><p>+ ad.content +</p>);

2. Stream ads in real-time from the AdResource REST endpoint

To stream the ads to the UI, we use our REST endpoint class. This also subscribes to the ads topic in Redpanda and streams presonalized ads.

package com.redpanda.gaming.personalization;
import com.redpanda.gaming.personalization.model.Ad;
import io.smallrye.mutiny.Multi;
import java.util.UUID;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;

public class AdResource {
  Multi<Ad> ads;
   * Endpoint retrieving the “ads” Redpanda topic and sending the items to a server sent event.
  @Produces(MediaType.SERVER_SENT_EVENTS) // denotes that server-side
events (SSE) will be produced
  public Multi<Ad> stream() {
	return ads.log();
} }

The Quarkus framework automatically configures connectivity with Redpanda by scanning the configuration properties provided in the file.

3. Set up the recommender module

The recommender module for our real-time ads personalization solution is a Quarkus microservice implemented as a Kafka Streams® application. It basically consumes player engagement events from Redpanda and recommends potential ads to a specific player. You can find the source code in the class.

import io.quarkus.kafka.client.serialization.ObjectMapperSerde;
import java.time.Duration;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.Topology;
import org.apache.kafka.streams.kstream.Consumed;
import org.apache.kafka.streams.kstream.Produced;
import org.apache.kafka.streams.kstream.TimeWindows;

public class AdRecommender {

  private static final String GAMING_EVENTS_TOPIC = “gaming.results”;
  private static final String AD_RECOMMENDATIONS_TOPIC = “ads”;

  private static final int AD_TRIGGER_THRESHOLD = 5;

  public Topology buildTopology() {
	StreamsBuilder builder = new StreamsBuilder();

	ObjectMapperSerde<GamingCompletedEvent> eventSerDe = new
	ObjectMapperSerde<Ad> adSerDe = new ObjectMapperSerde<>(Ad.class);, consumed.with(Serdes.String(), eventSerDe))
    	.filter((key, value) -> value.result.equalsIgnoreCase(LOSS))
    	.filter((key,count) -> count > 5)
    	.mapValues((key, value) -> {
      	return new Ad(key,
          	“Want to win next time?,
          	“Try buying the laser shooter weapon to easily secure a
win next time.
    	.to(AD_RECOMMENDATIONS_TOPIC, Produced.with(Serdes.String(),


We use the buildTopology() method to build the Kafka Streams topology. This topology is needed to process incoming GamingCompletedEvents from the gaming.results Redpanda topic. In this example, we simply count five consecutive losses of a player and recommend upgrading to a new laser weapon for a better experience next time. For that, the Kafka Streams topology first filters out only the LOSS events, groups them by playerID—which is set as the event key—counts the occurrences, and sends a new ad to the ads stream.

The JSON serialization/deserialization of incoming and outgoing messages is done using ObjectMapperSerde classes from the Quarkus framework. You can find the relevant POJOs inside the model package.

How the solution components communicate via Redpanda topics.
How the solution components communicate via Redpanda topics.

4. Run the personalized, real-time ads solution

You can run the solution components as regular Java applications. Make sure you have JDK version 8 or above installed on your local machine. First, start a single-node Redpanda cluster using Docker Compose by typing the following from the root level of the 3-ads folder:

docker compose up -d

Now, create the gaming.results topic.

docker exec -it redpanda-1 rpk topic create gaming.results

Then, start the frontend module as follows:

cd frontend
mvn quarkus:dev

Start the recommender module as well:

cd recommender
mvn quarkus:dev

At this point, the frontend is ready to receive real-time ads. Visit the frontend by typing http:// localhost:8080 in your browser.

Screenshot of the display for your personalized ads
Screenshot of the display for your personalized ads

Lastly, produce several GameCompleted events to simulate consecutive losses for the playerID 100.

docker exec -it redpanda-1 rpk topic produce gaming.results
Produced to partition 0 at offset 0 with timestamp 1661062751845.

Producing the same event more than five times triggers an ad and instantly renders it on the UI. The frontend should look like this:

UI displaying a personalized ad prompted by consecutive losses
UI displaying a personalized ad prompted by consecutive losses

You did it! Now you know how to set up a real-time personalization system that can serve relevant, in-game ads to your players for increased revenue.

Step into the future of gaming revenue with real-time, personalized ads

In this post, we expanded on our previous use case on how to gain actionable insights from your real-time gaming data.

Real-time ads are just one of several use cases game developers should master to keep pace with today’s data-driven gaming landscape. Although this post showcased a minimal yet comprehensive example, you can run with it and build even bigger, better applications. For example, you could augment it with machine learning to deliver highly tailored recommendations in real time. Redpanda helps you easily lay down the foundation for these advanced infrastructures—without racking up high operational costs in the long run.

For now, check out our documentation to learn more about Redpanda, and browse the Redpanda blog for more tutorials. You can also take Redpanda for a test drive and see for yourself just how fast and simple it is to run. If you get stuck or have questions for our team, join our Redpanda Community on Slack, or get in touch to find out how Redpanda can help you supercharge your game development.

Let's keep in touch

Subscribe and never miss another blog post, announcement, or community event. We hate spam and will never sell your contact information.