Pixeldoor

Docs

Everything the Godot and Unity plugins can do, and the HTTP API for any other engine. Use the key pxd_sandbox to try anything here: you get example cards and nothing is recorded.

Godot plugin

Project settings

Project, Project Settings, Pixeldoor.

SettingWhat it does
api_keyYour game’s key. Empty means test mode with example cards.
open_modeoverlay_store (default) opens the Steam overlay store page. overlay_webpage opens the tracked link in the overlay. browser always uses the browser.
send_contextOn by default. Turn off to stop sending platform, language and country.

Card properties

Select the card in your scene and change these in the inspector.

PropertyWhat it does
PlacementName of the screen, like menu or gameover. Shows up in your stats.
Sourcenetwork: games from other devs. mine: your other games, with your custom cards first. picks: the games you list in Picks.
PicksGame slugs for picks, like my-friends-game. Ask the other dev for theirs.
Fill From NetworkIf your picks aren’t available, show a network game instead of hiding the card.
Header Text / Button TextAnything you want. Empty uses the defaults.
Auto LoadLoad a game when the card appears. Turn off for screens that open later and call refresh().
Hide When EmptyHide the card when there’s nothing to show, like when the player is offline.

Ready-made scenes

All in addons/pixeldoor. Drag one into your scene and change its settings in the inspector. See where to put them for ideas.

SceneWhat it is
promo_card.tscnOne game. Turn on Compact for pause menus (smaller art, no tagline).
recommended_button.tscnA “Recommended games” button. Opens a popup with 4 games (set Count). Esc or Close shuts it, and it works while your game is paused.
promo_shelf.tscnA heading and a row of 2 to 6 games. Call refresh() for new picks.
thanks_screen.tscn“Thanks for playing” with a row of games and a button back to your menu. Loads new games each time it’s shown. Connect finished to go back to your menu.
demo_end_screen.tscn“Thanks for playing the demo” with a wishlist button. Set Full Game App Id; it works even if the full game isn’t in the network. Add Full Game Slug too if it is, and the clicks show up in your dashboard.
loading_screen.tscnA loading screen with a card. It loads your next scene in the background and switches the moment it’s ready, whether or not the card has shown up. The card is loaded ahead of time so it appears instantly, and only counts as seen after a second on screen.
world_poster_2d.tscnA poster for a 2D world, like a board in town. The player walks up and presses E (the interact action, added for you if your project doesn’t have one), or clicks it. Put your player’s body in the player group.
world_poster_3d.tscnThe same for a 3D world: a poster, a billboard, or the screen of an arcade cabinet (turn on Glow so it lights up). Counts as seen after a second on screen within 15 meters. Have your own interaction system? Call interact().

Each one has its own wording settings (title, message, button text) and the same card settings as above: Placement, Source and Picks.

Loading screen
# Show the loading screen, load the level in the background,
# and switch the moment it's loaded. The card never holds it up.
PixeldoorLoadingScreen.change_scene("res://levels/level_2.tscn")

# Your own loading code instead:
var screen := PixeldoorLoadingScreen.open()
screen.set_progress(0.5)   # 0.0 to 1.0 as you go
screen.close()             # the moment you're done

Custom cards

Make cards for your own news in your dashboard: a Kickstarter, your next game, a sale, your Discord or soundtrack. Run as many as you like, each on the screens you choose, and A/B test the wording. Any card with Source set to mine shows them first while they’re running, then your other games. Nothing to change in your game, and no patch when you change a card. Plugin 0.5.0 and later shows each card’s label and button text.

Script functions

The plugin adds a Pixeldoor autoload. The card uses these, and so can you:

CallWhat it does
await fetch_feed(count, placement, source, picks, fallback)Returns an array of games to show. Empty if offline or nothing matches.
await load_image(url)Downloads card art as a texture. Cached.
prefetch(count, placement, ...)Loads games and their art ahead of time, for a screen that opens later.
take_prefetched(placement)The games prefetch loaded, or an empty array. Each set is handed out once.
report_impression(game)Call once the card is visible.
open_store(game)Logs the click and opens the store page.
send_contextSet to false from code, for example from your game’s privacy setting.
request_failed(what, detail)Signal for network problems, if you want to log them.
GDScript
# Build your own card with the plugin's functions
func _ready() -> void:
    var games: Array = await Pixeldoor.fetch_feed(1, "pause", "mine")
    if games.is_empty():
        return  # offline or nothing to show: just don't show a card
    var g: Dictionary = games[0]
    $Title.text = g.name
    $Art.texture = await Pixeldoor.load_image(g.image_url)
    Pixeldoor.report_impression(g)              # once it's actually visible
    $Button.pressed.connect(func(): Pixeldoor.open_store(g))

Unity plugin

Unity 2021.3 or newer, any render pipeline. Download pixeldoor-unity-0.1.0.tgz, then Window, Package Manager, +, Add package from tarball. Text uses TextMeshPro when your project has it set up.

WhereWhat
Project Settings, PixeldoorAPI key, Open Mode, Send Context. Also under Tools, Pixeldoor, Settings.
GameObject, PixeldoorPromo Card (and compact), Recommended Games Button, Shelf, Thanks for Playing Screen, Demo End Screen. Each one is plain uGUI with the same Inspector settings as the Godot card.
Tools, Pixeldoor, Create Demo SceneEvery ready-made piece wired up with test cards. Press Play.
card.Refresh()For screens that open later: turn off Auto Load and call it when the screen shows.
C#
using PixeldoorSdk;

// Loads the scene in the background and switches the moment it's ready.
PixeldoorLoadingScreen.ChangeScene("Level2");

// Or with your own loading code:
var screen = PixeldoorLoadingScreen.Open();
screen.SetProgress(0.5f);   // 0 to 1 as you go
screen.Close();             // the moment you're done

Build your own card with Pixeldoor.FetchFeed, LoadImage, ReportImpression and OpenStore. With Steamworks.NET or Facepunch.Steamworks in your game, clicks open the Steam overlay; with IL2CPP stripping on, add the link.xml from the package’s README.

C#
using PixeldoorSdk;

var games = await Pixeldoor.FetchFeed(1, "pause", PromoSource.Mine);
if (games.Count == 0) return;                      // offline or nothing to show
myRawImage.texture = await Pixeldoor.LoadImage(games[0].image_url);
Pixeldoor.ReportImpression(games[0]);             // once it's on screen
myButton.onClick.AddListener(() => Pixeldoor.OpenStore(games[0]));  // also logs the click

HTTP API

Base URL https://pixeldoor.app. Send your key in the X-Promo-Key header.

GET /api/v1/feed

ParameterWhat it does
count1 to 6 games. Default 1.
placementScreen name, like menu.
sourcenetwork, mine or picks.
picksComma-separated slugs for source=picks.
fallback1 (default) fills from the network when picks are empty. 0 returns nothing.
platform, langOptional context, like windows and en. Send ctx=0 to skip context and country.
Response
{
  "serve_id": "9d75be5b-3fb5-488a-b093-dd570e4ba0fb",
  "source": "network",
  "games": [
    {
      "id": "a1b2…",
      "slug": "their-game",
      "name": "Their Game",
      "tagline": "One short line.",
      "steam_app_id": 1234560,
      "image_url": "https://…/header.jpg",
      "link_url": "https://store.steampowered.com/app/1234560/?utm_source=pixeldoor&…"
    }
  ]
}

source tells you what you actually got, which is network if a fallback happened. Custom cards come back first on source=mine with "kind": "custom" and optional label and button_text; report their impressions and clicks the same way. steam_app_id is 0 for games that aren’t on Steam.

POST /api/v1/events

Send impression once a card is on screen and click when it’s clicked. Batch up to 50 and send every few seconds, plus right away on a click. Duplicates are ignored.

Body
{
  "events": [
    { "serve_id": "9d75…", "game_id": "a1b2…", "kind": "impression" },
    { "serve_id": "9d75…", "game_id": "a1b2…", "kind": "click", "ms_since_shown": 4200 }
  ]
}

Opening the store

EngineCall
Godot (GodotSteam)Steam.activateGameOverlayToStore(app_id, 0)
Unity (Steamworks.NET)SteamFriends.ActivateGameOverlayToStore(new AppId_t(id), EOverlayToStoreFlag.k_EOverlayToStoreFlag_None)
Unity (Facepunch)SteamFriends.OpenStoreOverlay(id)
Unreal (C++)SteamFriends()->ActivateGameOverlayToStore(id, k_EOverlayToStoreFlag_None)
GameMakersteam_activate_overlay_store(id)

No overlay? Open steam://openurl/ followed by link_url if Steam is installed, or link_url in the browser if it isn’t.