Skip to content

In-Game Advertising

Nostr Game Engine supports native in-game advertising through its integration with the nostrads protocol.

Preparing the App

Before enabling ads, make sure your application is initialized with a valid appId (see NGEApplication).

The appId must correspond to a pubkey that has a valid metadata event containing a lnurl or lightning address (lud06 or lud16) for receiving ad revenue.

The easiest way to set this up is by using a Nostr client such as Primal. In your profile settings, you’ll find a field where you can set your lightning address:

Setting a lightning address in Primal

If you don’t yet have a lightning address for your app, here are some options (from simplest to more advanced):

  1. Blink.sv - custodial mobile wallet that provides a lightning address.
  2. Rizful - offers both custodial and non-custodial wallets with lightning addresses.
  3. AlbyHub - self-hosted non-custodial lightning node with a lightning address.
  4. LNbits - self-hosted interface that connects to various Lightning backends and can provide a lightning address via extensions.

Enabling Ads

To enable ads in your app, call the enableAds convenience method on NGEApplication:

NGEAppRunner appRunner = NGEApplication.createApp(appId, settings, app -> {
    // ... callback once app is ready ....
    app.enableAds();
    // ...
});

This will automatically:

  • Add an ImmersiveAdComponent
  • Initialize it with a default set of relays
  • Create a random advertisement key (an anonymous private key used to identify the current player in the ads network)

If you want to use a custom set of relays and/or a custom advertisement key, you can pass them explicitly:

NostrPrivateKey adsKey = NostrPrivateKey.generate();
app.enableAds(adsKey, List.of("wss://relay.ngengine.org", "wss://relay2.ngengine.org"));

Note

How you generate the adsKey is up to you.

  • You may generate a new random key for each play session.
  • Or you may persist it across sessions.

In either case, never link it directly to the player’s identity, to preserve privacy.

Setting Up the Scene for Ad Spaces

Next, you need to prepare your scene to detect ad spaces and display ads. This is done by adding an ImmersiveAdControl to the rootNode (or any other spatial in your scene):

ImmersiveAdControl adControl = new ImmersiveAdControl(assetManager);
map.addControl(adControl);

Once added, register the control with the ImmersiveAdComponent:

componentManager.getComponent(ImmersiveAdComponent.class).register(adControl);

Context-Aware Advertising and Ad Groups

The setup above is enough to start showing ads, but it won’t filter them by context or preferences. For that, use the extended ImmersiveAdControl constructor:

public ImmersiveAdControl(
      @Nonnull AssetManager assetManager,
      @Nullable List<AdTaxonomy.Term> categoryIds,
      @Nullable List<String> languages,
      @Nullable AdPriceSlot priceSlot,
      @Nullable String context // unused for now
) {}

Tip

You can obtain AdTaxonomy.Term instances either by:

Creating a new taxonomy instance:

AdTaxonomy taxonomy = new AdTaxonomy();

Or retrieving it from the component:

AdTaxonomy taxonomy = componentManager
    .getComponent(ImmersiveAdComponent.class)
    .getTaxonomy();

Then, fetch terms by ID or path:

Term term = taxonomy.getById("150");
Term term = taxonomy.getByPath("Attractions");

For a full list of taxonomy IDs and paths, see the Nostr Content Taxonomy CSV.

Multiple Contexts in the Game World

An ImmersiveAdControl only affects the spatial it’s attached to and its subtree.

  • If attached to the rootNode, it applies to the entire scene.
  • You can attach multiple controls to different children of the rootNode, each with different filters, allowing different ads in different areas of your game world.

Advanced Filtering

The ImmersiveAdControl also supports a custom filter via:

adControl.setFilter((AdBidEvent event) -> {
    // return true to accept, false to reject
});

This lets you implement complex client-side filtering logic, tailored to your app’s needs.

Adding Adspaces to 3D Models

Adspaces are 3D surfaces in your game world where ads can be displayed.

The ImmersiveAdControl automatically detects adspaces, applies the correct material, and loads ads as textures. However, you still need to define the geometry that will host each adspace.

Defining Adspaces Programmatically

You can create adspaces directly in code by adding a Geometry of any shape (commonly Quad or Box) and tagging it with the supported resolution:

Geometry adspace = new Geometry("AdQuad", new Quad(2, 4));
adspace.setUserData("nostrads.adspace", "256x512");

Here, "256x512" is one of the supported resolutions defined by the nostrads protocol. A full list of valid sizes can be found in the nostrads documentation (s tag).

Note

You must ensure the geometry’s aspect ratio matches the chosen ad size. Otherwise, the ad will appear stretched or distorted.

The preferred approach is to define adspaces directly in your 3D modeling tool (e.g. Blender).

  1. Create a geometry (e.g. a cube or plane) with the correct aspect ratio.
  2. Add the custom property:
    • Key: nostrads.adspace
    • Value: one of the supported resolutions (e.g. 256x512).

Resolutions must match the supported sizes defined by the nostrads protocol, in the nostrads documentation (s tag).

Tip

In Blender, when exporting to glTF, make sure to enable: Include → Custom Properties This ensures your nostrads.adspace property is exported correctly.