feedcade
Night Garden2 playsTug of Taps1 playsSticker Wall1 playsToday's Poll1 playsBrick Bloom0 playsStar Courier2 playsNeon Serpent2 playsComet Catch2 playsPond Hopper1 playsBubble Pop4 playsCrown Button4 playsPixel Wall4 playsNight Garden2 playsTug of Taps1 playsSticker Wall1 playsToday's Poll1 playsBrick Bloom0 playsStar Courier2 playsNeon Serpent2 playsComet Catch2 playsPond Hopper1 playsBubble Pop4 playsCrown Button4 playsPixel Wall4 plays

Documentation

The manual for games that live in the feed.

How one sentence turns into a hosted game, what the window.feedcade SDK hands every build, the limits that keep rooms quick, the HTTP endpoints underneath, and what happens on chain when you back a game with a coin.

Start

Overview#

feedcade writes a whole browser game from a short description, serves it from an isolated origin, and gives you a link that X unfolds into a card people can play without leaving the timeline.

A single file

Each game is one HTML document with its styles and scripts inline. There is nothing to install and nothing to bundle.

Shared by default

Every game owns a room: a JSON key-value store plus a live list of who is playing, the same for every visitor.

Coin optional

Tie a pump.fun coin to a game whenever you want. Its creator fees can be split between wallets on chain.

Start

Quickstart#

Everything happens in the browser. You never touch a build tool or a server.

  1. 1

    Write a sentence

    Describe the game on the home page and press build. The HTML streams into the studio as the model writes it, and the preview starts the moment it’s saved.

  2. 2

    Tweak it

    Ask for changes in plain language, one at a time. Each build is stored as a version you can go back to, and when the preview throws an error the studio offers to send it back as a fix request.

  3. 3

    Post the link

    Copy the share link (feedcade.xyz/s/…) into a post. X reads its player card and shows the game itself instead of a plain preview.

  4. 4

    Back it (optional)

    In the studio, Back it with a coin launches a pump.fun coin tied to the game. One wallet approval covers the whole launch.

high-five.html · a complete shared game
<!doctype html>
<html>
  <body>
    <button id="five">high five</button>
    <p id="count">0 high fives so far</p>
    <script>
      // window.feedcade already exists by the time this runs.
      const label = document.getElementById('count')

      feedcade.room.subscribe((state) => {
        label.textContent = (state.fives ?? 0) + ' high fives so far'
      })

      document.getElementById('five').onclick = () => {
        const now = feedcade.room.get('fives') ?? 0
        feedcade.room.set('fives', now + 1)
      }
    </script>
  </body>
</html>

Open that page in two tabs and press the button in one: the count changes in both. That’s the whole multiplayer model. The SDK keeps one shared object in sync and calls you when it changes.

Start

Writing prompts#

The builder already knows the card size, the SDK and where the badge sits. Spend your words on the game: the goal, the controls, and the part everyone shares.

prompt

A one-button lighthouse game: tap to sweep the beam and catch ships before they hit the rocks. Shared top-10 board with names.

prompt

A 32×32 mural everyone paints together. Eight colors, tap to fill a cell, strokes from other people appear live.

prompt

A daily five-letter word with six tries. Show how many visitors cracked today’s word and the average number of guesses.

prompt

Tug of war between team sun and team moon. Every tap pulls the rope; first side past the line takes the round, then it resets.

  • Say what’s shared. A leaderboard, a canvas, a tally, a crown. Naming it is enough for the builder to put it in the room.
  • Give it a mood. “Pastel and slow”, “CRT green, frantic” or “bubbly with lots of particles” shapes the result more than a list of features.
  • Change one thing per follow-up. Small, specific requests keep what already works intact.

SDK

window.feedcade#

The host adds one small global to every game before any of its own scripts run. There is nothing to import and no key to paste.

MemberTypeWhat it gives you
feedcade.sitestringThis game’s id. The same id names its room and its share link.
feedcade.player{ id, name, color }The person playing, remembered on this device.
feedcade.setName(name)stringRenames the player (trimmed, at most 24 characters) and returns the name kept.
feedcade.inXbooleantrue when the game is running inside a frame, such as a post.
feedcade.roomobjectThe shared key-value state every visitor reads and writes.
feedcade.playersobjectWho has the game open right now.
i
Treat window.feedcade as read-only. Don’t define your own object with that name; the runtime owns it.

SDK

Player#

On a first visit the runtime picks a random id, a guest name and a color, and stores them on that device. Coming back later keeps the same identity.

player.js
feedcade.player
// { id: "q3m8x1ka", name: "player-q3m8", color: "#7dd3fc" }

// Let people choose what the leaderboard calls them.
nameInput.onchange = () => {
  nameInput.value = feedcade.setName(nameInput.value)
}

// The card in a post is small, so tighten the layout there.
if (feedcade.inX) document.body.classList.add('in-post')

SDK

Room state#

One JSON key-value store per game. A write shows up on your own screen at once and reaches every other open copy on the next sync, usually within a second.

room.js
// Resolves after the first pull, with everything in the room.
const state = await feedcade.room.ready

feedcade.room.get('round')        // one key, from the local copy
feedcade.room.all()               // a copy of every key

feedcade.room.set('round', { n: 3, startedAt: Date.now() })  // any JSON up to 8 KB
feedcade.room.remove('round')     // same as set('round', null)

// Runs right away with the current state, then on every change.
const stop = feedcade.room.subscribe((state, changedKeys) => {
  if (changedKeys.includes('round')) drawRound(state.round)
})
stop() // unsubscribe
MethodReturnsNotes
room.readyPromise<state>Settles once the first pull finishes (or fails, with whatever is known).
room.get(key)value | undefinedReads the local copy. No request is made.
room.all()objectA deep copy of every key, safe to mutate.
room.set(key, value)Promise<{ ok, rev }>Updates locally first, then writes. Passing null deletes the key.
room.remove(key)Promise<{ ok, rev }>Shorthand for set(key, null).
room.subscribe(fn)() => voidfn(state, changedKeys) runs immediately and after every change. Call the return value to stop.
✓

Each key keeps the latest write

If two players write the same key at the same moment, one of the writes is lost. Give every player a key of their own, such as 'score:' + feedcade.player.id, and combine those keys when you draw.

SDK

Presence#

The players list shows who has this game open right now, with the name and color each person picked.

presence.js
feedcade.players.subscribe((people) => {
  here.textContent = people.length === 1 ? 'just you' : people.length + ' here'
  dots.replaceChildren(...people.map((p) => avatar(p.name, p.color)))
})

feedcade.players.list() // the last list received, no request

A visitor counts as present while their copy of the game is syncing with a subscriber attached. They drop off 20 s after their last sync. Lists hold up to 50 people, most recently seen first. Board thumbnails load with ?thumb=1 and never show up as players.

SDK

Recipes#

Patterns the builder reaches for most often. Each one keeps writes per player so nobody overwrites anybody else.

// One key per player: only your own best score is ever written.
function submit(score) {
  const key = 'best:' + feedcade.player.id
  const prev = feedcade.room.get(key)
  if (prev && prev.score >= score) return
  feedcade.room.set(key, { name: feedcade.player.name, score })
}

feedcade.room.subscribe((state) => {
  const top = Object.entries(state)
    .filter(([k]) => k.startsWith('best:'))
    .map(([, v]) => v)
    .sort((a, b) => b.score - a.score)
    .slice(0, 10)
  renderBoard(top)
})
i
Write on events that matter, like a finished run, a placed cell or a cast vote. Writing on every animation frame runs into the 40-per-10 s limit within a second.

SDK

Local storage#

Settings that only matter on this device belong in localStorage. Anything another player should see belongs in the room.

storage.js
localStorage.setItem('sound', 'off')
localStorage.getItem('sound') // "off"

// Some sandboxes refuse storage. When that happens the runtime puts an
// in-memory stand-in in its place: calls still work, nothing survives a reload.
Keep it inGood for
localStorageMute switch, control scheme, “tutorial seen”, this device’s own best.
feedcade.roomScores, votes, shared canvases, turns, anything other people need to see.

SDK

Limits#

Room for real games, with enough headroom that every room stays fast.

LimitValue
Key length1–80 characters
Value size8 KB of JSON
Keys per room5,000
Room writes40 per 10 s, per visitor, per game
Changes per pull1,000 (more: true means ask again)
Sync interval~0.9 s subscribed · 4 s idle · paused in hidden tabs
Presence20 s window · up to 50 listed
Builds30 per hour, per visitor

Platform

Playing inside X#

X plays a web page inside a post when the link’s page carries player card tags. Every share page sets them for you.

GET /s/<id> · the tags X reads
<meta name="twitter:card" content="player">
<meta name="twitter:player" content="https://feedcade.xyz/p/<id>">
<meta name="twitter:player:width" content="480">
<meta name="twitter:player:height" content="480">
<meta name="twitter:image" content="https://feedcade.xyz/api/card/<id>">
URLWhat it serves
/s/<id>The share page. Post this one: it carries the card and shows the game at full size.
/p/<id>The player X frames: the game’s HTML with the runtime added first.
/p/<id>?thumb=1Preview mode for boards: no splash, no presence, no play counted.
/p/<id>?v=NAn earlier saved version, as listed in the game’s history.
/api/card/<id>The still image X shows before someone taps play.
i

Design for a 480 × 480 square

The card is square and small. Scale your stage to min(100vw, 100vh) so the same game also fills a phone or a desktop window on the share page, and make every control work with a thumb.

Platform

Sandbox & badge#

Games run where they can’t reach anything that matters, and the host frames them with a short intro and a corner badge.

The player is served with a sandbox allow-scripts content security policy and without same-origin access, so each game gets an opaque origin. It can’t read cookies or storage from this site or from X. Its frame-ancestors list only allows X and feedcade itself to embed it.

Before the game appears, a feedcade splash shows for about 1.5 seconds; a tap skips it. A small badge then stays in the bottom-right corner, linking to the game page and, once the game has a coin, to its ticker. Both live outside the game’s own code, so a game can’t restyle or hide them.

480 × 480your gamebadgekeep clear ↓
The badge corner, roughly 120 × 34 px
!

Leave the bottom-right corner empty

Buttons or score text placed there end up under the badge. The builder already avoids that area; keep it in mind when you ask for layout changes.

Platform

Models#

Pick the model in the composer. Any follow-up can switch to a different one, and the history records which model wrote each version.

ModelLabStrength
Claude Sonnet 5.5AnthropicPolished, playable games that hold up on the first build.default
GPT-5.6 SolOpenAICareful with rules, scoring and physics that add up.Strong game logic
Kimi K2.7 CodeMoonshot AIQuick, tidy builds for small games and tools.Fast coder
Gemini 3.8 FlashGoogleThe quickest first draft, made for rapid tweaks.Quickest
GLM 5.3Z.aiLoud colors, bouncy motion and juicy effects.Playful visuals

Platform

Versions & remixes#

No accounts. The browser that creates a game holds its edit key; everyone else can play it or start their own copy.

edit key

Ownership lives in your browser

Creating a game returns a token once. The studio keeps it locally and sends it with every change. Lose it and the game stays playable, but nobody can edit it.

history

Every build is a version

Versions are numbered and kept. Open any of them with ?v=N, and restore one to make it current again without losing the others.

remix

Fork anything

Remix copies a game’s current HTML into a new game with a fresh id and your own edit key. The original is untouched.

Coins

Backing a coin#

Any game can carry one pump.fun coin, launched from its studio whenever you’re ready. The game becomes the coin’s website and the coin’s ticker joins the game’s badge.

  1. 1

    Open the studio

    On your game, choose Back it with a coin.

  2. 2

    Name it

    Name (up to 32 bytes), ticker (up to 10 characters), a description and an image. If you skip the image, generated coin art is used.

  3. 3

    Choose who earns

    Keep every creator fee for yourself or split them across up to 10 wallets.

  4. 4

    Add a first buy (optional)

    A dev buy goes in the same transaction as the launch, so it lands before anyone else can trade.

  5. 5

    Approve once

    Phantom or Solflare asks for a single approval that covers the coin, the fee split and the optional buy.

i
Expect roughly 0.03 SOL in network rent plus whatever you put into the dev buy. feedcade never holds keys or funds: the server prepares each transaction, and your wallet is what signs it.

Coins

Fee splits#

Creator fees are routed by pump.fun’s own fee-sharing program, so payouts happen on chain with no feedcade server in the loop.

split · basis points
[
  { "wallet": "7fUq…you",    "bps": 6000 },  // 60%
  { "wallet": "Hx2m…artist", "bps": 2500 },  // 25%
  { "wallet": "3kPa…prizes", "bps": 1500 }   // 15%
]
// Up to 10 wallets. The bps must total exactly 10,000.
!

A split can’t be edited later

The launch transaction revokes the split’s admin, so nobody, including you, can add, remove or reweight wallets afterwards. Check addresses before you approve.

Coins

Coin pages#

Each backed coin gets a page at /coin/<mint> with a live chart, recent trades and a buy and sell panel.

On the curve

While the coin is still on its bonding curve, trades go straight to pump.fun’s program.

After graduation

Once it graduates, buys and sells are routed through Jupiter for the best available price.

Checked before you sign

The server builds each trade and verifies every program it touches against an allowlist. Your wallet signs.

Reference

Room API#

The same two endpoints the SDK calls. Public, CORS-enabled, no key required.

GET/api/room/:id?since=:rev
POST/api/room/:id
# Everything on the first call, then only what changed after a rev
curl "https://feedcade.xyz/api/room/pixel-wall?since=0"

# Write a single key (any JSON up to 8 KB; null deletes it)
curl -X POST "https://feedcade.xyz/api/room/pixel-wall" \
  -H "content-type: application/json" \
  -d '{"key":"px:4,7","value":"#fde047"}'

Response

200 OK
{
  "rev": 842,
  "changes": {
    "px:4,7": "#fde047",
    "best:q3m8x1ka": { "name": "player-q3m8", "score": 31 },
    "px:0,0": null
  },
  "more": false,
  "players": [
    { "id": "q3m8x1ka", "name": "player-q3m8", "color": "#7dd3fc" }
  ]
}
FieldMeaning
revThe room’s current revision. Send it back as since to receive only newer changes.
changesKeys changed after since. null means the key was deleted. With since=0 only live keys are returned.
moretrue when more than 1,000 changes are waiting. Request again with the new rev.
playersOnly included when you identify yourself with p, n and c (player id, name and color). Doing so also counts you as present.

A POST takes {"key": string, "value": any JSON} and answers {"ok": true, "rev": n}.

Try it

GETfeedcade.xyz/api/room/pixel-wall?since=0

This calls the real room behind the demo pixel wall. The first send returns every key that has a value. The box then moves since to the returned rev, so the next send only brings back what changed in between. Paint a square on the wall in another tab and send again to watch it arrive.

Reference

Games API#

Create, build, read and fork games over HTTP. Changing a game needs the edit key returned when it was created.

POST/api/sites
POST/api/sites/:id/generate
GET/api/sites/:id
POST/api/sites/:id/remix
POST/api/sites/:id/restore
GET/api/sites?sort=new|popular|mcap
CallSendGet back
POST /api/sites{ prompt, model }201 with { id, token }. The token is only shown this once.
POST /api/sites/:id/generatex-site-token header, body { prompt, model? }. The first build may leave out the prompt and reuse the one it was created with.The HTML as text/plain while it’s written, ending with a marker line: <!--feedcade:saved:vN--> or <!--feedcade:error:…-->.
GET /api/sites/:idnothing{ site: { id, title, prompt, html, version, model, plays, … }, versions: [{ version, prompt, model, createdAt }] }
POST /api/sites/:id/remixnothing{ id, token } for the new copy.
POST /api/sites/:id/restorex-site-token header, body { version }Makes that version current again.
GET /api/sitessort, limit, offsetA page of games: new, popular or mcap.
const base = 'https://feedcade.xyz'

// 1. create the game and keep the edit key
const { id, token } = await fetch(base + '/api/sites', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ prompt: 'a two-player reaction duel', model: 'anthropic/claude-sonnet-5.5' }),
}).then((r) => r.json())

// 2. build it; the body streams back as the model writes
const res = await fetch(base + '/api/sites/' + id + '/generate', {
  method: 'POST',
  headers: { 'content-type': 'application/json', 'x-site-token': token },
  body: '{}',
})
const text = await res.text()
const end = text.match(/<!--feedcade:(saved|error):(.*?)-->/)
console.log(end[1] === 'saved' ? base + '/s/' + id : 'failed: ' + end[2])

Reference

Coin data API#

Market data for coins backed on feedcade. Mints that were never paired with a game here answer 404.

GET/api/sites/:id/coin
GET/api/coins/:mint?kind=market
GET/api/coins/:mint?kind=candles&tf=5m
GET/api/coins/:mint?kind=trades
coin.sh
# The coin behind a game (each field is null until it exists)
curl "https://feedcade.xyz/api/sites/<id>/coin"
# -> {"coin": {...} | null, "market": {...} | null, "fees": {...} | null}

# Candles for the chart
curl "https://feedcade.xyz/api/coins/<mint>?kind=candles&tf=1h"

Reference

Errors#

Every failure is JSON shaped like {"error": "what went wrong"}, with a status that tells you what to do next.

StatusWhen
400The body is malformed, a prompt is missing, a key is outside 1–80 characters, or a timeframe is unknown.
403The x-site-token header is missing or doesn’t match the game.
404No game with that id, or a mint that isn’t backed on feedcade.
413The value is larger than 8 KB of JSON.
429Too many room writes or builds. Back off for a moment and retry.
502An upstream service (pump.fun, Jupiter or the model provider) didn’t answer properly.
507The room already holds 5,000 keys. Delete some to make space.

Reference

FAQ#

Do I need to know how to code?
No. You describe the game and ask for changes in plain words. The SDK and these docs are here for anyone who wants to read or tweak what the builder produced.
Who is allowed to change my game?
Only a browser holding its edit key, which is the one that created it unless you copied the key elsewhere. Anyone else can play it or remix it into a separate game of their own.
Is room data private?
No. A room is readable and writable by anyone who knows the game’s id, through the game or the public API. Never put secrets, keys or personal details in it.
Why does my link show a plain preview on X?
X fetches and caches the card itself, and a brand-new link sometimes needs a moment. Wait a minute and post it again. The game always plays on its share page regardless.
Can a game load libraries?
Yes. The builder may pull pinned versions from jsDelivr or unpkg. Beyond that, a game talks only to its own room.
Can I change the fee split after launch?
No. The split’s admin is revoked in the launch transaction, which is what makes the split trustworthy for everyone listed in it.
Does every game need a coin?
Not at all. Most games never have one. Backing is optional, and it’s available at any time from the game’s studio.

Got an idea for a game?

Describe it in a sentence and it can be playing in a post in a few minutes.