- 14 merges to main — 4 staging→prod releases (#1727 #1747 #1759 #1792) and 10 hotfixes; 5 engine releases; 6 Telegram prod releases in one week.
- 14 Go Live presses, 8 succeeded — four failures and two cancels, every one of them a rollout-tooling or prod-health fault, each restored cleanly. Root causes below.
- One incident: portal-cms connection-pool starvation (5, 6, 8, 9, 12 Oct) — every public read answered 500 for hours at a time; fixed 12 Oct, follow-ups shipped 14 Oct.
- Creator Round 2 (six Google Search campaigns) live since 12 Oct — $1,776 → 177 Game Created ($10.03) by 15 Oct; health check 17 Oct, spend stops 23 Oct, read 26 Oct.
- Customer feedback is live on prod and answering: ~33–36% of shown prompts get a response; top complaint "Controls felt awkward" — the same thing 14 of 20 mobile playtesters said.
⭐ Highlights
1 · Production promotions unblocked — and what the rollout tooling cost us
An IAM grant on 9 Oct ended the block; the week then exposed four more faults in the 4 Oct rollout rewrite, each fixed the day it bit.
- 9 Oct 08:4xZ —
roles/logging.viewergranted to the Actions service account (the "Observe 10%" step could never read Cloud Logging). live#1727 promoted 10 → 50 → 100 with the check passing three times; 11/11 services at 100%; migrations 113–115; the first prod wizard-config sync from code returned 200. - 9 Oct 18:21 / 21:37 — two presses cancelled at the 45-min ceiling:
check_errorsvetoes on the first 5xx anywhere at 100%, and one drop-out beacon for a deleted project 500'd (an FK violation latent since May, ~2/day). live#1753 returns the 204sendBeaconexpects; #1755 keeps browser services on the stable revision until the final 100% (users were getting HTML from one build and chunks from another at 50/50). - 12 Oct 18:14 — the gateway was blamed for 5xx it forwarded from the wedged old portal-cms; live#1773 lets the rollout promote a fix during an outage.
- 14 Oct 11:33 / 11:59 — a full-list migration run re-executed 111's
DROP/CREATE TRIGGERonanalytics_events, stalled 16 min behind the hourly rollup and wedged live player-api; the retry died on a second copy of the abandoned-normalization guard. live#1793 fixes the guard; rule from now: one migration file per dispatch. - live#1767 (in #1792): slowapi failed closed — a rate limiter that couldn't reach Redis 500'd every request including
/health, on cold canaries and once on live engine-api; all nine FastAPI services inherited it. - The ledger: 12/12 green promotions before the 4 Oct rewrite; 18 runs after it, 8 green. Fix list written (
docs/+ memory): rehearse the script on staging canary tags, one guard in one place, gate on error rate vs baseline, rollback as its own job, IAM preflight as the CI principal.
2 · Incident: portal-cms pool starvation (5 → 12 Oct)
/api/contests/*, /api/access/settings, promo campaigns and ads config answered 500 "connection pool exhausted" for hours on five days; the contest page rendered that error to the public.
- Cause: prod portal-cms had a 2-slot pool per instance and one instance; each LiveOps Product Journeys open (fired in pairs) held a slot for minutes, the hourly rollup held one for 5–10 min at the 300 s timeout, and the 25 s statement timeout never applied — PgBouncer's
DISCARD ALLwiped the session-levelSETbefore the first real query. - live#1771 (12 Oct 19:36) separate heavy pool for analytics and cron, public pool 5, per-transaction
SET LOCAL statement_timeout· #1773 single-flight cache on Product Journeys, rollup one day not two, 900 s request window. - live#1781 (13 Oct) the real cap then broke the hourly rollup five runs straight:
retention_d1was a correlatedNOT EXISTSover all ofanalytics_events— rewritten 219.6 s → 2.1 s on a prod-sized copy; 12 Oct's frozen metrics backfilled. - live follow-ups in #1792: #1786 dashboard requests the report once · #1787 write routes release their connection before purges, syncs and LLM calls · #1788 hourly precompute into
product_report_snapshots(CMS 0014). - Open: #1788's precompute cannot finish on prod data — the 7-day FUNNELS query runs past the 600 s budget (quadratic correlated subqueries over a materialised CTE); scheduler job paused 14 Oct. The on-demand Product Journeys route has never completed on prod; it needs a hash-join rewrite timed on a prod-sized snapshot. Post-mortem: doc.
3 · Performance improvement for webviews
Telegram shipped six times to prod; the shared scene-loading menu is on by default for playmusic.gg.
- live Telegram #62 (R1, 9 Oct): named-game links open and auto-start; CDN thumbnail renditions took Discover from 62 MB to 0.7 MB; shared engine cache path so the second game re-downloads 0 KB; no game-HTML redirect hop; one XP read instead of three.
- live #79 (R3) multiplayer as a lazy chunk, minified critical path · #87 (R4) boot deferrals (gtag + MP past the first screen), lean feed rows, SDK ETag, 60 s
/api/gamescache · #88 (R5) Appearance, the engine loading menu, For You and Create on prod — first load 50 KB gz. - live#1741 scene loading on by default for playmusic.gg (engine #573 carries the presentation); #1699/#1724 modal hands loading to the engine menu, engine preloaded on arrival.
- live engine #576 deduplicated foliage images + bundled standalone deps · #575 elapsed time preserved under frame caps, render work trimmed · #577 a dead model URL can no longer hold the boot.
- staging Telegram round 5 (#92–#94: first screen, feed latency, Browse 120 rows first) and round 6 (#95–#97: engine warm manifest, shipped disabled).
4 · Performance stability for webviews
Silent render freezes on in-app browsers can now be detected and recovered — gated off until physical acceptance.
- live#1758 + engine #579/#580 (11 Oct): silent render-stall detection for recovery-capable hosts, independent default-off gates for Facebook/iPhone and Android WebViews, multiplayer excluded, diagnostics + retry.
- live#1802 + engine #584 (today): opaque cross-origin
Script error.no longer reported as a fatal boot failure; load-timeout reports carry the engine build, the pending readiness blocker (world/config_audio/orchestrator) and stage timings; the engine retries transient config and audio downloads. - live engine #582 the Play card shows the right controls on first paint · #570 pause button scoped to Drops · #569 flight preview no longer seats the boot runner.
- live Telegram #73 (R2): engine flat crash recorded as
engine_errornottimeout; 7-day last-good feed served when the catalog is down;pm_tracksurvives the engine's config-fetch abort (missing track title + per-song leaderboard on Chrome).
5 · Website and game loads
- live#1737 small versioned preview clips fill the edge cache after a Range miss (MISS → HIT verified live); song audio kept 72 h in the browser, 1 h at the edge.
- live#1745 asset URLs resolved at serve time instead of backfilling stored copies — all 4,812 public catalog rows verified free of raw
storage:////api/minio/URLs · #1750/api/songslimit raised to 500 so curated carousels render. - live#1755 No Brakes campaign list rendered empty — the frontend resolved its 12 games against the first 500 catalogue rows; now pages to completion. #1740 drafts survive sign-in; song promotion hardened; repair script for the metadata it left missing.
- open#1803 CMS catalog waits for asset-service to start instead of failing.
6 · Alchemy
- live the first-time walkthrough shipped (9 Oct, #1721) and grew: #1777 "Create a new game" as its closing step · #1778 it counts the step it is on · #1749 a prompt when returning from a published game · #1748 Exit follows the visitor's trail back, not always Home.
- live#1779 song ships load in the preview · song-generated vehicles offered in Flight and Drift, one "Made for This Song" card · a game cannot be published twice · #1762 the intro gate reaches pre-consent paid arrivals and runs in Alchemy (paid-only, no holdout,
creation_gate_evaluatedlogs every decision) — the gate Round 2 needed. - What the numbers say: Round 2 wizard converts 25% of starts to a made game (≈$9/maker) vs Alchemy 13% (≈$17); Alchemy's loss is desktop — $39/conversion on 169 desktop clicks vs $11 mobile, finish-step 35%. Desktop playtesters: "configuring, not creating" (11 of 26).
7 · Telegram
- live six prod releases: #62 R1 fast-launch (9 Oct) · #73 R2 post-release fixes · #79 R3 size · #87 R4 size + load · #88 R5 Appearance, loading menu, For You + Create on prod · #91 header/Settings polish — all 13 Oct; last prod dispatch 13 Oct 20:15Z. #46 was closed and replaced by #62 pinned to a frozen sha.
- live creator handoff (#1732/#1733 in #1759): the token travels in the wizard URL fragment, cleared after a successful create, callback gated and scrubbed from Sentry — callbacks stay disabled until verified with a real issued handoff.
- staging the account-link workstream, inert behind flags: #98 bounded shell XP writes · #99 start + status routes · #100 PMH callback routes · #101 LinkOutbox binding; PMH side open #1798 XP writer lock · #1799 Sentry scrub · #1800 link tables. Telegram staging auto-deploys on push (52 commits ahead of main); prod is a manual dispatch.
8 · User feedback — live, and already telling us something
- live customer feedback shipped 9 Oct (#1723/#1725, migrations 113–115) and was extended every release: #1746 impressions as the response-rate denominator (116) · #1751 campaign + variant on each respondent · #1774 a creation survey chain (preview → created → own-game play) · #1775 why a survey was never shown (117) · #1776 six random survey groups incl. a control · #1783/#1784 the wizard asks on the preview screen.
- Prod, since impressions went live 12 Oct: game-ended 36 of 37 issued were shown, preview 87 of 91 — the earlier "shown 15 of 136" was missing data, not unseen surveys. Response rate ~33–36% of shown. 13 Oct snapshot: preview 30 responses (15 good / 11 okay / 4 poor), game-ended 25 (9 / 11 / 5), top reason "Controls felt awkward".
- Playtests (PlaytestCloud 20 mobile · Antidote 26 desktop): use-again mobile Wizard 8 / Alchemy 4 / either 4 / neither 4; desktop W10 / A8 / either 1 / neither 7. Themes (mobile/desktop testers): didn't know what to do 18/9 · lag 15/9 · controls awkward 14/5 (Drift Drive named by 11) · thin catalogue 11/8 · "configuring not creating" 7/11. Expectation predicts the verdict: came for a rhythm game → match 3.75 (14/18 would use); came for a builder → 2.72.
- By design,
game_createdgets no survey whilepreview_readyis sampled (one prompt per visitor per cooldown) — a CMS decision if creation feedback is wanted.
9 · Song pipeline
- staging the pipeline runs on Inngest (8 Oct, with local dev + docs), the old pipeline's D1 sync fixes carried over, one reusable sized-small deploy for the services, finished runs reported to Slack with a section per failed step (#1795/#1804), a step yields to an earlier run on the same song, character and slot meshes on a song without a source image (#1805), theme from a closed profile (#1796). Hosting / vendor-limit / account-split decisions recorded as ADRs.
- live#1740 song promotion hardened (partial promotions rejected, pooled connections released) · #1745 promotion copies preview assets before publishing new version URLs · CMS files every character variant under
characterand shows the rigged one. - open #1801 Asset Studio phases 1–3 — song-independent asset creation (upload GLB → validate → review → publish; generated assets; rig + clips), migration 120, CMS pages, ADR 0008 · #1797 pick the character pose and prompt per regenerate · #1782 keep the prod
-nextservices warm (the 61 s cold start that 504'd Drops uploads) · #1769 compressed model variants.
🚀 Release log
9e7c956b (15 Oct 10:56Z) · engine prod = the main Pages alias · Telegram prod = manual dispatch, last 13 Oct 20:15ZPlatform
| Date | Release | What | Traffic |
|---|---|---|---|
| 15 Oct | #1802 | filter opaque boot errors; load-timeout diagnostics (direct to main; engine #584 companion) | live 10:56 |
| 14 Oct | #1792 + #1793 | 16 PRs — feedback chain/unseen/groups (#1774 #1775 #1776 #1783 #1784), CMS pool follow-ups (#1786 #1787 #1788 #1780), limiter fail-open (#1767), takedown guard by product (#1766), Alchemy (#1777 #1778 #1779), Horror Survival label (#1790), back-merge (#1785); migrations 117 + CMS 0014; #1793 normalize-retry fix | live 12:53 after two failed presses |
| 13 Oct | #1781 | hotfix: retention_d1 rollup in one pass (219.6 s → 2.1 s) | live 08:09 |
| 12 Oct | #1771 #1773 | hotfixes: heavy pool + per-transaction timeout; analytics runaway guards, rollup one day, 900 s | live 19:36 (18:14 press failed) |
| 12 Oct | #1762 | hotfix: intro gate for paid first visits, wizard + Alchemy, ?msg= — unblocked Round 2 | live 11:12 |
| 11 Oct | #1759 | player render-stall recovery gates (#1758), asset delivery (#1737 #1745), Telegram handoff (#1732 #1733), No Brakes landing coverage (#1757), feedback attribution (#1751), create-success cleanup (#1742); carries #1753 + #1755 | live 19:39 |
| 9 Oct | #1753 #1755 | hotfixes: drop-out beacon 204 for an unknown project; campaign lists page the catalogue; browser services switch only at 100% | live 11 Oct |
| 9 Oct | #1747 | 7 PRs — drafts through sign-in + song promotion (#1740), Sky Rider character in preview (#1744), Alchemy exit/return (#1748 #1749), /api/songs 500 (#1750), survey impressions (#1746, migration 116) | live 11 Oct (two 9 Oct presses cancelled) |
| 9 Oct | #1741 | hotfix: scene loading on by default for playmusic.gg | live 11:13 |
| 9 Oct | #1727 | 19 PRs held since 2 Oct — customer feedback (#1723 #1725), Alchemy FTUE (#1721), Drops Play v2 follow-ups (#1714), modal loading handoff (#1699), crash-report retention (#1700), feed preload (#1724), prod wizard-config sync (#1701), rollout fixes (#1702 #1707); migrations 113–115 | live 08:30 — first promotion since 2 Oct |
| 8 Oct | #1728 | hotfix: the Observe step retries its Logging query and prints gcloud's reason — which surfaced the IAM gap | live 9 Oct |
Engine (live on merge)
| Date | Release | What |
|---|---|---|
| 15 Oct | #584 | retry transient config and audio downloads (companion to #1802) |
| 14 Oct | #583 | Play card shows the right controls on first paint (#582) |
| 11 Oct | #580 | gated silent render-stall detection, multiplayer excluded (#579) |
| 9 Oct | #578 | frame-cap timing (#575), foliage dedup + standalone bundle (#576), boot no longer held by a dead model URL (#577); smoke budget 30 min (#574) |
| 9 Oct | #573 | 21 PRs — scene-loading presentation + per-mode loading (#554–#567), iOS/Android WebView safeguards (#555), Drops preset Play v2 (#551 #562), HUD reliability (#552 #553), Vector Squadron (#560), flight shadow (#561), Drops pause scope (#570), flight preview runner (#569), shadow disc (#571) |
Telegram (integrations → main; prod by dispatch)
| Date | Release | What |
|---|---|---|
| 13 Oct | #91 | hotfix: header polish, gear → Settings, Appearance switches |
| 13 Oct | #88 | R5: Appearance on, engine loading menu (VITE_LOADING_UI=scene), For You landing, Create opens the playmusic.gg wizard; sound on by default |
| 13 Oct | #87 | R4: boot deferrals (#80), size round 3 + SDK ETag (#81), Safari 14 CSS target (#82), build nits (#84), lean feeds + 60 s cache (#85), Browse pages (#86) |
| 13 Oct | #79 | R3: minified critical path (#75), dead CSS (#76), multiplayer lazy chunk (#77 #78), shell_boot_failed (#71), handoff token in the fragment (#74, inert) |
| 9 Oct | #73 | R2: post-release fixes (#63), last-good feed (#64), login widget + cache/security headers (#65), engine crash classed correctly (#66), pm_track survives config abort (#69), scene loader fatal-only (#70); 404s no-store (#72) |
| 9 Oct | #62 | R1: fast-launch (#43–#45 #47–#53), UX quick wins (#39), contract v1 (#40), theme switch (#41, hidden), loading handoff (#42) |
📥 Inbound
- Platform staging → main is entirely the Inngest song-pipeline work above plus devcontainer wrangler and an asset-catalog MCP fix; main is back-merged (#1794). Nothing user-facing is waiting.
- open against staging, worth a look this week: #1801 Asset Studio · #1791 forged-guest XP, unauthenticated
/endpayouts, guest convert/claim — this is the unowned guest-claim takeover from the 29 Sep hold assessment · #1806 Drops records the share code a sign-up arrived with · #1803 catalog cold start · #1798–#1800 Telegram link (PMH side) · #1797 character pose per regenerate · #1782 warm-nextservices · #1769 webp/compressed models · #1768 (conflicting, superseded in part by #1771). - Engine open: #572 Beat Snake mode · #581 single landing page for creator user testing · #543 Forge visual editing workspace · #525/#531 Block Jam + Beat Swing · #538 street racer · #542 LOOP territory playtest.
- Integrations: Telegram staging carries rounds 5–6 and link L0–L2b1; the next prod dispatch is a decision, not a merge.
Next 7 days
- Round 2 health check 17 Oct (day 5): run the first-party cohort pull (
r2_cohort_d4.sh— prod reads need the user), fold into the Day-4 doc; candidate actions from the read: drop the +15% desktop bid, add "how to" bare-term negatives, decide GB. Spend stops 23 Oct — a manual pause; read on 26 Oct. - Product Journeys on prod: rewrite the FUNNELS SQL hash-join style, time it on a prod-sized local snapshot, then un-pause
portal-cms-precompute-product-journeys-production. Until then the LiveOps dashboard has no prod data. - Rollout tooling: start the fix list — contract tests on recorded gcloud output, one drift guard in
plan, error-rate gate, rollback as its own job, IAM preflight as the CI SA. Migrations: one file per dispatch; add an applied-files ledger +lock_timeout. Tidy the six stale 0%rollout-*tags on the gateway. - Security: review and ship #1791 (guest-claim takeover); the admin-key delete route from the same assessment still has no PR.
- Render-recovery gates (#1758 / engine #580): physical Facebook-iPhone and Android WebView acceptance before enabling; Telegram callback acceptance before enabling "Play it" DMs.
- Feedback: decide whether
game_createdgets its own sampling; read the first week of survey groups vs control. - Still owed: the 23 SuperTokens accounts orphaned Jul 27 → Sep 15; source maps for Sentry.
🔍 Monitoring
Creator Round 2 — six Google Search campaigns, live since 12 Oct ~11:30Z
$1,776 → 177 Game Created at $10.03 (Ads, to 15 Oct 12:50Z) · first-party (14 Oct pull, 65% click-id coverage): 615 landed → 271 started (44%) → 123 makers (166 games) → 92 played own → 30 claimed ($29.57/creator) → 14 shared → 3 finished a song.
- Tool: wizard 26 makers / 105 starts (25%, ≈$9) vs Alchemy 17 / 135 (13%, ≈$17); Alchemy loses at the finish step (35%), the wizard at start. Alchemy's loss is desktop: $39/conv on 169 desktop clicks vs $11 mobile.
- Market: Tier A (India-led) $2.43/maker, 22 of 30 sign-ups, 11 of 14 shares; Nigeria 14 makers of 22 arrivals. GB weak — 228 clicks → 16 conv, $30 vs US $13.65, ZA $14.23; A-Song GB 40 clicks, 0 conv. Mobile cheaper per game overall ($11.85 vs $21.72) — the +15% desktop bid adjustment is buying the worse half.
- Terms: day 1 had ~16% of spend on Roblox/Minecraft/coding searches → 24 phrase negatives (now ~2%). Bare "how to" terms convert ~3% vs "…for free" 15–44%. Known-age clicks convert 18% vs unknown 8%; 18–24 cheapest ($8.57).
- Docs: Creator Round 2, Day 4 (framed by funnel stage, playtest overlaid) · plan + stop rules. Read with care: 11 of 11 days are not in; surveys stand at 13 Oct; the intro gate reopens after a reload for visitors who haven't answered consent (pre-existing).
WZ No Brakes TikTok UA — the traffic is Pangle automation, not people
83,111 TikTok-arm sessions 1 → 14 Oct · 82% referred by pangleglobal.com (ByteDance's in-app ad network) · CA 96.8% · median 2 s on page · 0.25% played.
- Game-tap funnel: 1,771 taps → 988 loaded → 143 played (8.1%); one iPhone in a news-app WebView = 1,357 visits / 568 taps = 32% of all taps; top 10 devices 44%; 56% of taps from devices with ≥10 visits.
- The Instagram arm is real: 3,576 sessions, 68% of taps play, 18% of sessions play, median 6 s.
- Any site-wide visit/session figure since 1 Oct is dominated by this — split
wz_ua_tiktokout first. The fix is a placement exclusion (Pangle / audience network) in TikTok Ads — a campaign decision; spend is not in our data.
Deploy reliability, by the numbers
- 28 Sep → 4 Oct (inline workflow): 12 presses, 12 green, 6–13 min each. 4 → 14 Oct (rewritten script): 18 presses, 8 green, 7 failed, 3 cancelled at the 45-min ceiling. This week: 14 presses, 8 green — every failure traced and fixed the same day (IAM, FK 500, forwarded 5xx, migration lock, duplicated guard). Builds: 34 runs, 1 failure.
- A successful graduated promotion now takes ~24 min (freeze → smoke → normalize → 10% → 50% → 100% → verify → tags → wizard-config sync); the 100% step alone is ~15 min because each
update-trafficblocks until Cloud Run reports Ready.
Daily metrics — repaired, watch the 00:00 UTC run
- The hourly rollup 504'd at 300 s every hour from 7 Oct; after #1773 it failed five runs on
retention_d1; after #1781 it completes in seconds. 12 Oct's frozen metrics and the 11 Oct cohort's retention row were backfilled by hand. Anydaily_metrics-sourced play or retention figure for 7–13 Oct should be re-read before quoting. - Song engagement across all 174 songs now has a report (30 d to 12 Oct; picker exposure = distinct picker sessions, not events — events over-count by a quarter from remounts).
song_sectionsv16 went live 8 Oct, so "since re-layout" is the comparable window.
- Production is serving the 2 Oct build (#1673). 17 promotions 23 Sep → 2 Oct; none since.
- On canary at 0%: #1685 #1687 #1697 (29 PRs) #1708 — three go-lives failed on 7 Oct at the 10% shift (fixed by #1708), a fourth and a fifth (7 Oct 16:02, 8 Oct 14:58 on #1728) failed at "Observe 10%": the Actions service account has no Cloud Logging role, so the candidate-error check is refused with
PERMISSION_DENIED. Not a code fault — an IAM grant unblocks it. Every failure restored prod cleanly (~30 min). - Inbound: integrations #46 open · platform staging 41 commits, no PR yet · engine staging 17 PRs, no PR yet.
- Paid tests concluded 5 Oct; every Google campaign paused.
⭐ Highlights
1 · Performance improvement for webviews
The second game in a Telegram session no longer re-downloads the engine; skies, textures and stems got lighter.
- staging#49 shared engine asset path (
/game-proxy/_e/): the second game re-downloads 0 KB of engine instead of ~1.74 MB. Also fixed games after the first never leaving the loading screen (a chainedpostMessagewrapper). - staging#48 CDN thumbnail renditions · #47 direct links, hero-first
/start, menu button · #51–#53 per-frame message realms, early chrome, one XP read, loader stages. - staging#42 opt-in engine loading handoff (
?loadingUi=scene) for the Discord, TikTok and Telegram players. - live engine #549 shared GLB textures before GPU upload · #540 skies via Cloudflare resizing, stems skipped for non-stem OSU games, traffic cars preloaded only for drift modes.
- live#1676 stems at 192 kbps — a 3.5-minute game's 34 MB download cut ~40%.
- staging#1699 GameModal hands its overlay to the engine menu · #1724 engine preloaded on arrival, feed artwork and position kept through a load · engine #554–#567 per-mode scene loading.
2 · Performance stability for webviews
Graphics caps and context recovery for iOS and Android WebViews, and a load that always ends in Play, Retry or an error — never a spinner.
- live engine #548 iOS WebView graphics bounded, lost context recovered.
- staging engine #555 Android WebView graphics cap, startup deadlines, constrained-startup recovery guards.
- canary#1685 (= #1683 + #1684) a failed app asset leaves bounded reload evidence on the next boot; branded iOS in-app browsers get a browser-choice sheet behind
inapp_interstitial— off by default, 48 h of diagnostics first. - canary#1671 load progress stays until the engine says
PLAYABLE(WORLD_READY could precede decoded music); homepage single-click launch gated · #1674 retry panel when a load budget expires, audio diagnostics kept · #1677 the panel shows before a game URL exists. - live engine #522 waits for song + config readiness · #544/#546 playback readiness and load-phase timings · #545 per-asset audio failure diagnostics.
- staging#1700 fatal crash reports kept after timeouts, click ids scrubbed from Sentry · open#1712 web-storage polyfill · #1711 a slug embed's load counted once.
3 · Performance improvement — website and game loads
/foryou starts fetching at first byte, the edge serves byte ranges and cached shells, and Drops stops pulling 28–40 MB of clips below the fold.
- canary#1694 feed data and route chunk start at first byte on
/foryou· #1696 paid links (ttclid,list,tour,lpt) keep the shell cache; the games-list cache no longer advertises 4 h of browser freshness. - live#1555 slow-connection loading —
modulepreloadwith retries, next-song preload waits for playback, no speculation on Save-Data / 2G / 3G. - live#1568 edge: byte ranges on
/playmusic-assets/(416, If-Range, HEAD); anonymous/foryou,/create,/alchemyHTML reused at the POP for 30 s. - live#1627 Drops: clips stream only while at least half on screen; signed-in routes lazy;
/meprobe in parallel; self-hosted fonts; CDN-resized tiles; a stale tab reloads once. - live#1621 prosumer lookups compare ids as
uuidso the indexes serve them. - open#1709 the CDN answers
If-None-Matchwith 304 instead of resending the file.
4 · Alchemy
Fixes on every mode, a measurable Wizard-vs-Alchemy split, and — on staging — a guided first build.
- live#1549 Obby builds a real course; no human in the car modes; a picked ship stays · #1550 Merge has no character and its song reaches the engine · #1551 Publish once per press · #1591 Keep pills removed · #1616 Shuffle's pick is the preview's model.
- live#1605/#1607 the create split (
create_split):/createnever asks, the split routes · #1669 the draw stamped on every event (it had been landing on one racingfeature_flagsrow) · #1617 slot counts mapped onto the wizard funnel. - live#1666 the preview play the publish gate waits for is logged — 47 paid arrivals filled all four slots and 6 published (28 Sep – 1 Oct); the 41 who stopped can now be split into never-played vs played-but-didn't-publish.
- staging#1721 directed walkthrough for a first-time creator: Song → Game → Character → World/Sky → Preview → Publish, one lit box at a time, the cue on the whole picker; owns no state, publish gate untouched.
- staging#1691 Mode first, and a default that is really selected · #1713 Zombie Drive previews as itself, not Drift · #1715 a Music Aimer backdrop stays out of worlds; Obby's sky lives in the World slot · #1722 song picks and builds on LiveOps' per-song tables.
5 · Telegram
Twenty PRs on staging, one open release, and the first Telegram Ads funnel read (see Monitoring).
- open#46 staging → main, 55 commits: #39 UX quick wins · #40 contract v1 · #41 light/dark theme · #42 loading handoff · #43–#45 fast-launch row 1 + auto-start. Its head is the staging branch, so #47–#58 ride along.
- staging#54 staging multiplayer through a same-origin relay passthrough (engine endpoints only, shell credentials stripped) · #55 the host's real name on the guest's lobby · #56 the proxy patch's
pm_*messages no longer count as the engine speaking · #57 join a Browse game outside the portal catalog; a withdrawn room cancels a pending join; a stale lookup can't override a newer choice · #58 group/startopens the app, inline results recorded. - On merge, Discord prod auto-deploys; Telegram and TikTok prod are manual dispatches. Before merging: the WIP Appearance panel (
c1f79fe), a redcontract-scantest, a staging Discord secret reset, an MP-retry smoke. - live#1568 the Telegram preview-clip engine ported to the portal shell (per-portal previews, migration 110) · canary#1679 Card Clips settings in the CMS.
6 · User feedback
An in-product sentiment prompt with an Ops inbox — on staging, off by default.
- staging#1723 prompts after a preview, after creating, after two consecutive creation failures, and after completing a song or game; ratings reveal reasons; hidden during gameplay, creation and auth; partner embeds and Drops excluded · #1725 after game over.
- staging CMS → Growth → Customer Feedback (
/admin/feedback): per-event summaries, filters, review status, issue tags, notes, CSV, editable questions, sampling and frequency. ADMIN-only; the respondent snapshot comes from the submitting session, never the request body. - staging migrations 113 / 114 / 115; disabled by default; pilot 25% sampling, 7 days between invitations; 3,149 unit + 21 integration + 6 Chromium tests.
- Feedback-driven fix batches: Drops Play v2 QA PV2-3 → 15 (staging#1714) · Drops UI F-01 → 08 (live#1639) · SB-21 → 24 (#1621) · GL-7 / GP-2 / GP-9 (#1643) · Telegram S42 → 44 (#55, #57).
7 · Song pipeline
Lyrics, uploads and stem size fixed on the legacy pipeline; the Inngest pipeline sits behind a switch that is inert on prod.
- live#1567 Synced Lyrics: exact lookup → free-text search → duration ranking · #1572 undecodable uploads skip ffmpeg · #1645 bulk upload no longer dies on a 33 MiB batch (an HTML 413 hidden by a JSON parse) · #1647 artist and title from
Artist - Title.mp3and tags. - live#1676 Replicate stems at 192 kbps · #1654 runtime switch legacy ↔ Inngest (
song_pipeline_next_enabled) — no-nextservice on prod, so inert there · #1661 migration guard, after the partition repair nearly discarded 13.1k events. - branch
feat/inngest-song-pipeline: #1710 an isolated chart-analysis worker reused across songs · #1717 CMS character buttons generate a rigged character · #1719 thumbnail re-rendered after a merge · open#1692 cold start — a staging Drops upload 504'd on 5 Oct when the-nextservice took 61 s against the proxy's 60 s. - staging#1718 an existing song can re-enable 3D assets and skyboxes · #1706 Drops upload acceptance verified without consumer catalogue publication.
🚀 Release log
main Pages alias, so engine releases are live on mergeWhy nothing has reached traffic since 2 Oct
- 4 Oct #1687 rewrote promotion (frozen revisions, the same ones 10 → 50 → 100%, real rollback) after finding the old 10% step used
--to-latest— 100% immediately. Its own deploy run failed on merge. - 7 Oct #1697 merged 08:36; go-live failed 11:33, 12:18, 12:38 — all at "Shift selected revisions to 10%", each rolled back.
- 7 Oct #1708 hotfix, tooling only:
update-traffic --format=jsonreturns a list and the script required a dict, so the split was applied and then discarded as a failure; an aborted run'srollout-*revision tripped the drift check on every retry. Three new tests fail on the old script. - 8 Oct #1728 (cherry-pick of #1726) retried the Logging query ×3 and finally printed gcloud's answer:
PERMISSION_DENIED: Permission denied for all log views … authenticated as github-actions@playmusic-staging.iam.gserviceaccount.com. The SA holds run.admin, cloudsql.client, artifactregistry.writer, cloudscheduler.admin, iam.serviceAccountUser — and no logging role. Tuesday's "blip" was the same refusal with its stderr dropped. - Owed: grant
roles/logging.vieweronplaymusic-stagingto that service account, then Deploy + Go Live onmain(promotes the 3–7 Oct canaries), then back-merge main → staging or the next release reverts #1708/#1728. Before promoting: the portal-cms canary00316-petcarries a stray third container (playmusic-portal-cms-1, old image, no port, inert) — redeploy portal-cms or remove it. Worker and Drops changes from those releases are already live — they deploy at merge.
Platform releases
| Date | Release | What | State |
|---|---|---|---|
| 7 Oct | #1708 | rollout tooling hotfix (above) | canary |
| 7 Oct | #1697 | 29 PRs — Drops Play v2 (#1686 #1658 #1664 #1670 #1656 #1667 #1662 #1688 #1680) · load reliability (#1671 #1674 #1677 #1659 #1683 #1684) · feed (#1693 contest inline on paid landings, #1694, #1675) · analytics (#1660 paid cohorts + identity reconciled, #1672 msclkid, #1668, #1665, #1695) · CMS (#1679 Card Clips, #1690) · tooling (#1661 #1657 #1663). Nothing to migrate. | canary Drops + worker live |
| 5 Oct | #1687 | rollout safety rewrite, 242 tests | canary |
| 3 Oct | #1685 | reload diagnostics + default-off iOS browser choice (#1683 #1684) | canary |
| 1–2 Oct | #1666 #1669 #1673 #1676 | hotfixes: Alchemy preview play · create-editor draw · Microsoft msclkid as a click id (88 of 224 Alchemy paid landings had none) · stems −40% | live |
| 1 Oct | #1650 | Drops player accounts, scores, multiplayer invites (#1646) · October partition repair (#1652, migration 112 — prod had no analytics_events_2026_10) · Contest Rules in Promote-to-Prod (#1648, 0013) · promo same-tab + an open redirect closed (#1655) · TikTok/Meta pixels in CSP (#1644) · uploads (#1645 #1647) · pipeline switch (#1654) | live |
| 30 Sep | #1640 | Drops UI F-01 → 08 (#1639) · play accounting (#1638) · performance (#1627) · two Google Ads conversions for the paid player KPIs (#1642) · legal slugs at the edge (#1625) · CMS manual lists | live |
| 30 Sep | #1635 | play/create product measurement + migration 111 (#1612) · sign_up/login, run_index, play_context (#1614) · OAuth sign-in merges the guest's games (#1619) · four ordered dance routines (#1611, engine #530) · worker purge auth (#1613) + per-env KV keys (#1618) · radio scoped to /foryou + creation (#1633) · mute pauses the run (#1629) · shelf cap 12 (#1634) | live |
| 30 Sep | #1631 | ?tour=off | live |
| 28–29 Sep | #1605 #1607 #1610 | listen test ready: ?song= starts the station · the create split · weighted split links 75/25 (#1609) | live |
| 28 Sep | #1590 #1592 #1597 #1599 | remix on older flight games (#1589) · Keep pills removed (#1591) · two autoplay hotfixes (#1596, #1598 + #1600) | live |
| 27 Sep | #1581 | song feed as /foryou's default side with autoplay + first-tap fallback (#1577) · ?radio=quiet (#1579) · ?radio_card (#1580) · first-run tutorial (#1570 #1578 #1584) · arm pins for 30 days + test plan + Sky Rider D1 drift fixed (#1569) · #1567 #1571 #1572 #1576 | live |
| 25 Sep | #1568 | 51 commits — For You desktop layout as the / landing-split variant (#1554) · slow-connection loading (#1555) · radio + remix behind the features section, off on prod (#1561) · remix (#1559 #1560) · Alchemy #1549–#1551 · edge byte ranges + shell cache · AGENTS.md (#1553) · README (#1556) | live |
Engine releases
| Date | Release | What | State |
|---|---|---|---|
| 3 Oct | #550 | iOS graphics cap + bounded context reload (#548) · shared GLB textures (#549) | live |
| 3 Oct | #547 | playable readiness + audio-load diagnostics (#544 #545 #546) | live |
| 30 Sep | #541 | dance routine slots (#530; #534 rolled back the pose alignment) · play-path performance (#540) · a vehicle picked mid-build kept (#537) · TIME_UPDATE survives a backwards clock (#536) · debug seek off on prod (#539) | live |
| 28 Sep | #528 | coaster ends on the shared results screen and reports the score (#527) · True Platformer self-contained, no road (#529) | live |
| 25 Sep | #524 | merge board + run-end (#521) · startup readiness gate (#522) · Floor is Lava restart (#523) · Music Runner Play Again (#526) | live |
| — | #551–#567 | Drops preset Play v2 (#551 #562) · HUD dirty-check + reliability (#552 #553) · scene-loading handoff + per-mode loading (#554 #556 #558 #559 #563–#567) · Android WebView guards (#555) · Vector Squadron mode (#560) · flight shadow (#561) · canary timing (#557) — no release PR | staging |
📥 Inbound — the staging → prod PRs
Integrations #46 — open since 7 Oct 17:39
- Everything on integrations staging: #39–#45 and the ride-along #47–#58 (Telegram, above). 55 commits, 148 files, +15,400.
- Before merging (its own checklist): confirm the WIP Appearance panel (
c1f79fe) is meant to ship · fix or accept the redcontract-scan › setSettingsHandlertest (3 vs 2, predates #42) · reset the staging Discord client secret (briefly exposed in a staging bundle; staging only) · smoke Discord retry with the MP gateway on. - On merge: Discord prod auto-deploys (
packages/discord/**,packages/shared/**); Telegram prod is atelegram-deploy.ymldispatch withenvironment=production; TikTok manual. Scene loading stays opt-in.
Platform — the next release, not yet opened
- Staging is 41 commits past main (#1691 → #1725): Alchemy walkthrough + fixes (#1691 #1713 #1715 #1721 #1722) · customer feedback (#1723 #1725 — migrations 113/114/115 before portal-cms) · Drops Play v2 follow-ups (#1714) · modal loading handoff (#1699), scene-loading review on for staging (#1705) · crash-report retention (#1700) · feed preload (#1724) · CMS art gate (#1718) · LiveOps Creation tab (#1720) · stray
node_modulessymlink (#1716) · Drops upload acceptance (#1706) · a localization and market-expansion plan. - #1701 prod never synced wizard-config from code, so new flags never reached it — this is why the iOS browser-choice toggle was missing from the prod CMS. Plus its Doppler-action pin fixes.
- Already on main via #1708: the rollout fixes #1702 #1707.
mainis 1 commit ahead — back-merge first, then a merge commit. - open against staging: #1703 / #1704 visit-lifecycle events so a page killed by its host still leaves a row (TikTok preloads) · #1709 · #1711 · #1712.
Next 7 days
- Unblock and promote the 3–7 Oct canaries. Grant Logs Viewer to the Actions service account (one IAM command), clean the stray container off the portal-cms canary, then Deploy + Go Live on
main; keepinapp_interstitialoff and read 48 h of reload diagnostics first; back-merge main → staging afterwards. - Open the platform release as a merge commit after the back-merge; run 113/114/115 in the canary window before portal-cms takes traffic; run the #1701 wizard-config sync so prod sees new flags.
- Ship integrations #46 once its four items are cleared; dispatch Telegram prod; verify the second-game engine download is 0 KB on a phone. Open an engine staging → main PR for #551–#567 so #1699/#1705 have their other half.
- Turn on customer feedback in Ops at the 25% / 7-day pilot; confirm embeds and Drops stay excluded; invitation counts are issued prompts, not impressions.
- Decide the Inngest cut-over per environment. #1692 (cold start) blocks Drops uploads on the
-nextservice; the switch is per-env and prod has no-nextservice, so staging can go first. - Still owed: the 23 SuperTokens accounts orphaned Jul 27 → Sep 15 · the two auth-route findings from the 29 Sep hold assessment (confirm closed or schedule) · source maps for Sentry.
🔍 Monitoring
Paid tests concluded — every Google campaign paused 5 Oct 12:50 UTC
$9,987 Google since 1 Aug · 20,323 clicks · 274 creators · ~$18 per creator on Create campaigns (creators = the "Game Created" action only; metrics.conversions also counts Games Played).
- Wizard Top 11 $22 / creator (impression share 12.5%, 60% lost to rank) · Wizard ZA $6 · Wizard 18–34 $10 · Alchemy ×3 $30 · Listen radio $1,018 → 0 creators (~$8 per listener).
- Last week's $3,120 split 30 / 38 / 32 wizard / Alchemy / Listen → 63 / 36 / 1 creators.
- Microsoft (account export, no API): Search $2,907 · 3,071 clicks · CPC $0.95 · wizard 1,120 people → 152 created (13.6%, matching Google's 14.0%) · ~$18 per creator · Alchemy 117 → 8.
- Verdict: lean in on wizard search and Microsoft wizard; lean out of paid Alchemy and Listen.
- Caveats: Microsoft clicks were invisible to every paid read until #1673 (2 Oct); Google served zero impressions from 12:00 PT on 1 Oct until the next morning with no change-history cause — a one-day gap, not a tracking fault.
Create expectation gate — clean read, no effect
14.9% vs 14.5% made a game, shown vs not shown (±3.6 pts).
- First-time campaign visitors who entered the wizard, first touch 22–28 Sep PT, outcomes to 5 Oct: shown 677 vs 792 → chose a type 31% / 32% → configured 16% / 15% → made a game 14.9% / 14.5% → claimed an account 1.0% / 2.4%.
- 26% left the gate without an exit event. The holdout is still never logged; consent gating broke eligibility from 29 Sep, so the series ends there.
- Correction to the Sep 24 tab:
intro_experiment.pyreads the Ads[intro=1]trial (12 clicks, ended), not this gate.
The creation loop closed once
568 → 53 → 62 → 1 created → shared → strangers played → a stranger then created (24 Aug – 5 Oct).
- 53 creators shared 62 games; 62 strangers played a user-made game (15 games); one of them created (28 Sep, arrived via a shared link).
- Paid Google wizard: 348 created → 221 played their own (64%) → 96 finished (28%) → 30 shared (9%) → 8 games got a stranger play.
- The
shareevent keys the game ascontent_id, notproject_id— a join on the latter returns zero silently.
Surfaces and channels
- Plays / people / runs finished: playmusic.gg 2,003 / 714 / 80% · CrazyGames 1,321 / 964 / 70% (weeks of 14 + 21 Sep only, nothing since 24 Sep) · Playgama 358 / 160 · PlayHop 227 / 129 (197 in the last week) · FRVR 144 / 70 · Telegram 69 / 11 · Discord 65 / 14 · Y8 stopped 27 Sep.
- Unpaid traffic is 14% of people, 47% of plays, and returns 3× as often.
- The owned site's
portal_idis the literal'playmusic', not NULL.
Telegram Ads, 28 Sep – 1 Oct
318 → 22 → 1 opened → started a play → completed a song.
- 318 opened → 281 saw games → 123 clicked a game (237 clicks) → 22 started a play (6.9%) → 17 ended one → 1 completed a song. 77% of game clicks never became a play.
- Identity in the mini-app is
telegram_user_id(client_fpsees ~18 of 338 openers). - The portal logs no
visit_start, heartbeats, load stages or run events — no engaged time, load diagnosis or completion rate. The fast-launch loader stages (#53) and loading handoff (#42) start to close that.
Measurement repairs this fortnight
- October's analytics partition was missing on prod (found 1 Oct): a future-dated row in the default partition blocked its creation and the cron reported success. Migration 112 repaired it; the cron now ensures current + next month and answers 500 on partial failure. The repair's first run nearly lost 13.1k events to the dedupe trigger — hence #1661.
- Microsoft click ids captured since 2 Oct (#1673).
user_identifycarries the SuperTokens id, notusers.id. - WebView stability from Sentry: No Brakes dominates iOS load timeouts; one Instagram iPad white-screen near-miss — the #1684 sheet is built for exactly that, still off.
- Seen during the 8 Oct go-live window, on the live build:
POST /api/cron/rollup-daily-metricshit the 300 s request timeout (504) at 15:00 — the daily-metrics rollup that feeds this report's play counts is timing out; checkdaily_metricscoverage for October before quoting it.
/alchemy, split direct+organic traffic at / between home and /foryou, and stamped the arm on every event. #1527 (Sep 22) turned on crash and load-attempt telemetry for 100% of prod and shipped the create expectation gate. #1542 / #1545 / #1548 (Sep 23) fixed survival asset picks, rebuilt /games from the Front Page carousels, wired Drops consent, and took three attempts to stop the CookieYes banner painting over partner embeds. Engine gained real error capture; Telegram got its own GA4 property and a For You feed. Staging is nearly empty: two Alchemy commits.
⭐ Highlights
Sep 19 — release #1495 and the landing split at /
The big one landed at 04:34 (merge commit — a squash would have inherited [skip ci] from the wizard_config snapshots and skipped every Action): Drops SICK redesign P-0→P-8, Alchemy character/song/tile fixes and XP parity, /foryou chrome + Game Lists behind ?list=, Sky Rider options + collectibles, ad click-id capture (migration 109), PlayMusic Approved + bulk upload (108), ffmpeg normalisation, meshy-6-lite, real ESLint on both frontends. Migrations 107/108/109 ran 04:36; gateway 100% at 05:24 after a failed first promote. #1503 (05:36) fixed /alchemy serving the retired engine studio — AlchemyRoute fell back to the engine view whenever creation_flows.alchemy was false, which prod has; the route now ignores the flag and only the /create picker consults it. #1508 (19:03) shipped the landing split: direct and organic visitors to / are assigned at the edge to home or /foryou via a 30-day pm_lx cookie with a hop crumb; Googlebot, paid clicks (gclid) and Playgama referrals are exempt, so / stays the indexed SSR page. It also versioned the dead-R2-host repair scripts that had already fixed 2,421 catalog rows on prod. #1510 (19:27) stamped landing_variant on every event to both sinks — the split had been assigning prod visitors for 24 minutes with nothing joining an arm to an outcome.
Sep 22 — release #1527: crash telemetry on for 100% of prod, create expectation gate
Every game load now has a load_attempt_id and emits a stage event per step (open → engine_url_ready → iframe_loaded → engine_first_message → world_building → world_ready → playing), so a failed load is attributable to a stage instead of inferred from an absence (#1519). Crash reporting is narrow and opt-in — four kinds, no tracing or replay, SDK out of the bundle unless VITE_SENTRY_DSN is set — and that variable was set on Sep 21, so it went live with this deploy; #1524 opened connect-src for the ingest host. #1525 records auth failures that block generation; #1529 keeps the E2E/smoke suites out of Sentry (navigator.webdriver) and tags their analytics rows automation. Engine-side, #513/#514 add the hooks the engine never had: window error / unhandledrejection, a guarded Babylon Engine constructor, WebGL context loss and restore, and a render-stall escalation sampled from engine.frameId — fatal only for boot failures and a confirmed stall, so a game that keeps running never lands behind the host's error overlay. Promoted as engine #517. The create expectation gate (#1521–#1526): one screen between a paid click and the wizard, first-visit campaign arrivals only, 50/50 against a holdout, once-only, touches no wizard state. It also fixed two things in the shared Modal: focus containment (Tab walked out of every dialog in the product) and an elevation prop. Plus album-art upload from the CMS song page (#1520, with a worker carve-out), a failed CSS chunk preload now treated as a stale-deploy reload (#1528), and ffmpeg failures logging the tail of stderr — Sentry issue PYTHON-B8 had 4,000+ events that all read "ffmpeg version 5.1.9 … built with gcc" (#1530).
Sep 23 — releases #1542, #1545, #1548: survival picks, /games, Drops consent, and the embed banner
#1532: the config-builder worker's survival preset was applied with a blanket Object.assign, overwriting the collectible and chaser the creator picked in Step 3 — every published Horror Survival game shipped the default heart and Zed_1. The preset now fills only missing fields; games published before the worker shipped need a republish. #1534: /games grouped by the legacy projects.category string that nothing on the Front Page reads; the SPA and the crawler render now both read wizard_config.home_sections, one chip per carousel. Five slugs (drift, shooter, obby, flight, platformer) exist as both a carousel and a legacy category and the carousel now wins, so those indexed hubs shrink to the carousel's picks (staging: drift 45→6, shooter 44→3). #1533/#1535: Alchemy's Share button was createGame() with none of Publish's gating and a second project row per press — removed; pickers stop closing themselves; obby's Iso camera survives the apply. #1540/#1543: Drops gets a CookieYes banner wired to Consent Mode v2 — until now nothing ever granted consent, so every Drops GA4 hit was a cookieless ping and GA4 showed 0 events over 7 days; the script loads async after a stalled cdn-cookieyes.com was holding first paint. #1538: pm_arm and intro captured on first touch and stamped on every event — on Sep 23, 0 of 26,792 prod events carried intro. The embed banner took three releases. Yandex reported the CookieYes banner painting over the game in the PlayHop shell. #1541 (in #1542) pushed gtm.blocklist: ['cvt_KDQSW'] above the snippet on embedded surfaces — works on staging, not on the prod container. #1544 (release #1545, 22:01) gated the CMP per platform profile (features.consent true for web only; playhop gets a profile) with the sandboxedScripts token, verified against the live prod container — and the banner still showed. #1547 (release #1548, 23:05) found the real cause: prod builds carry a Google Ads snippet injected by vite.config.js that pushes three gtag entries before main.jsx runs, and GTM only honours a blocklist that precedes them; staging has no Ads id, so it could neither reproduce nor prove. The blocklist is now unshifted to index 0. Canary verified 23:15 (dataLayer[0] = blocklist, no CookieYes request, GTM + GA4 loading); promoted 23:12–23:20. #1548 also carried a nine-commit Drops batch (SB-13…SB-20 + a prosumer-api song_id::uuid compare so the 4-second status poll uses its index) that reached staging by a direct push — a mis-set upstream, since fixed.
Telegram: its own GA4 property, a For You feed, and per-launch attribution
Telegram analytics moved off the shared main-site GA4 property (where prod co-mingled with playmusic.gg on one stream) onto a dedicated property with four web streams — player/creator × prod/staging, each Measurement ID baked per build mode; the creator stream had been dead (#37). A traffic_type=internal QA flag keeps prod-stream testing filterable. #36: a For You feed screen — CSS scroll-snap with one IntersectionObserver, no gesture code, like the site's — with rankFeed as a fixed pipeline (pinned → curated-first → stable category-affinity sort → a diversity swap so no two neighbours share a song; the catalog has three "Tinfoil Hats"), a GET /api/foryou reader, every event attributed to the launch that started it, and the SDK + fonts served first-party so a filtered network can still boot. Earlier: /play opens the Mini App on the browse grid, and the prod webhook secret is set so the bot answers /start (#33–#35).
Also shipped
Five overnight hotfixes straight to main (Sep 18–19)
PG-85: context menu, selection and drag suppressed on the portal chrome, the consumer embed (the frame the shell cannot reach) and, engine-side, the whole game field with a text-field exemption (#1493, engine #510). Strict-lifecycle portals: the modal sends HOST_CAPS so the engine stops granting free revives there, and the HUD lives display is driven off the revive grant rather than FREE_REVIVES (#1496, engine #511). Back-merged before #1495 so the release didn't conflict in GameModalV2.jsx.
Engine
#509 (Sep 19, pairs with #1495): Drops UI theme preset + data-ui-theme stamp with a Drops-scoped flatten, self-hosted Anton/Spectral; Sky Rider creator-enabled collectibles with picked models, belly-to-earth rest pose, lean HUD, configurable off-beat wording, negativeFeedback config; Audio Flyer values chords like Music Runner, draws the whole chart, a colour collect is a collect; shooter boss gets real HP and opening spawn HP is capped to what the player can fire; Vector Beat Arena's loudest section stopped spawning (#500); coaster honours resultsMode 'continue'; a dead rig is caught instead of freezing the character, with a script that detects skin weights that tear the mesh; GLB sub-mesh transforms applied to vegetation instance matrices. #515–#518 (Sep 22): the aimer renders the backdrop image in Mania and Taiko; the Catch backdrop scrim is configurable.
Drops batch (SB-13 → SB-20)
Back from Style keeps the character pick; the preview names every game type, not 9 of 16; the success screen and the song page share the built game, not the song; the song-level Result card is gone; songs open in the basic view and return to Studio; GTM events no longer inherit the previous event's params; success-screen GA events name their song; creator's name casing kept in the greeting (SB-12); Rename-song allows editing the artist (SB-9); iOS fixes for monochrome glyphs, fixed-sheet shift and the marquee loop (SB-5/6/7).
Smaller
Alchemy on Sep 19: Play Preview shows the scene before the overlay covers it; Play swallowed the first press then went black then trapped; Tile Mode is Drop-only and Publish keeps it; Platformer stops spawning obstacles it doesn't have; the preview camera resets; the four slots stay on screen on mobile. Thumbnail backfills authenticate their show_all read; 122 catalog has_thumbnail rows repaired on prod Sep 22. Modal reads elevation consistently. Dev container for F5-debugging one API (#1419) and a write-jira-task skill (#1537/#1539) — no runtime effect.
On staging
Nearly drained
#1549: Alchemy's Obby builds a real course rather than a level strip; no human in the car modes; a picked ship stays picked. Open against staging: #1546 (the one lint error the SB-20 change left), #1536 (homepage promo popups on the /foryou landing), #1518 (song-pipeline revision), #1490 (tracks upload as file + JSON metadata).
Next 7 days
Promote the config the code is waiting on
Much of #1495 is config-driven and dark on prod until wizard_config staging → prod is promoted through the CMS: Sky Rider options and entity slots, Alchemy definitions, feed lists, promo popup, contest CTA. Prod has no wingsuit/coaster rows at all. Then D1 0025_add_song_genre by hand + Sync to Edge in Game Library, or the music-genre list filters match nothing. Then republish the Horror Survival games published before the #1532 worker shipped. The coasterSkyRiderDefinitions test has been red since Sep 19 because #1483's staging D1 edit was never made — a CMS edit of the staging wingsuit row, not a code change.
Make the intro-gate experiment readable, then fix what the phone screenshot showed
In this order: (1) emit the assignment event for the control arm in creationIntroGate.js so a holdout leaves a record — a handful of lines, and until it lands the experiment returns nothing; (2) collapse the song banner on the game-mode step (h-[40vh] in StepSongPreview.jsx) — CSS only, four or five modes above the fold instead of one and a half; (3) geo-target the cookie banner in GTM; (4) re-measure the mode step alone. Keep the ads-side intro override out so the product's 50/50 stays clean.
Experiment plumbing before spend
The CMS split links (split_destinations) have never carried prod traffic; Experiment 1's Wizard-vs-Alchemy comparison needs one live. Confirm client_fp is populated on prod rows — still no fix in any release this window. Source maps for Sentry: sourcemap is off and nothing runs sentry-cli, so every crash report points at minified offsets.
Consent follow-ups the PRs named
A durable GTM trigger exception on pm_surface equals embed, then drop the blocklist line. CookieYes dashboard: mark the SuperTokens cookies Necessary, keep Support GCM on. Ask the Yandex reporter to re-check the draft. A playhop partner-registry key.
Still owed from Sep 15
Reconcile the 23 SuperTokens accounts orphaned by the Jul 27 → Sep 15 /me regression — no commit or script for it in this window.
🔍 Monitoring
Landing split at / — live and readable since Sep 19 ~19:56
Direct + organic arrivals at / are split between home and /foryou; landing_variant on every event in GA4 and analytics_events.event_params. Visitors assigned between 19:03 and the #1510 promote have a cookie but no stamped events — unreadable, not backfillable. Paid, Googlebot and Playgama traffic is exempt by design, so this reads organic behaviour only.
Create expectation gate — live since Sep 22, and not yet readable as an experiment
The banner is the treatment, not an obstacle: the question is conversion with it against without, with CTA click-through as the intent signal. Early paid-cohort numbers at the game-mode step (Sep 23 pulls, DB access has since been blocked): never saw it 79 → 43 chose a mode (54%), 23 made a game; saw it 29 → 12 (41%), 3 made a game. Dismissed 17 → 8 (47%); did not dismiss 12 → 4 (33%). Of 16 shown the banner, 4 clicked the CTA (25%), 8 dismissed it, 4 did neither and left. Directionally lower with the banner; not significant (p ≈ 0.23) and not to be acted on. Three things stop this being a real read, two fixable this week: (1) the control arm does not record itself — describeIntroAssignment is only called from CreationExpectationGate.jsx, which only renders when the banner shows, so a holdout visitor emits nothing; the only holdout rows are four the ads override forced, and the 79 "never saw" are holdouts, returning visitors and segment exclusions mixed together. (2) the ads-side intro parameter overrode the product's assignment for two days, forcing the banner onto people assigned to control — removed Sep 23, so the 50/50 runs clean from then. (3) 29 vs 79 is too small either way; ~400 per arm to detect a 10-point gap. The fix for (1) is a handful of lines in creationIntroGate.js — emit the assignment event for the control arm at the moment it is assigned. Until it lands the experiment consumes traffic and returns nothing. #1538's intro stamp and scripts/analytics/intro_experiment.py still stand for the treatment side.
Mobile wizard: the song banner owns 40% of the screen at the moment we ask for a choice
Screenshotted on a 664-px phone viewport: song banner 266 px (40%), "Choose Game Mode" heading ~80 px (12%), yellow tutorial callout ~150 px (23%), game modes ~170 px (25%) — one and a half of twelve modes visible. The banner is h-[40vh] sm:h-[45vh] in StepSongPreview.jsx, so it claims the same share of every phone regardless of size, and it was still an empty black box eight seconds after load. The cookie banner then lands at 3.2 s and covers the bottom 57%, starting 20 px below the song banner; for anyone who hasn't dismissed it the picker is effectively gone — which is what the strong geography correlation was measuring. That is the second problem, not the first, and the earlier read-out had them the wrong way round. Collapsing the banner on the mode step is a CSS change and would put four or five modes above the fold; geo-targeting the cookie banner is a tag-manager setting; then re-measure that single step — what exists is a strong correlation across three markets, not proof. The rest of the readout is unchanged: the cost movement, the loop journey, and the stable bad news that about half of games are never played.
Crash + load-attempt telemetry — first week of data
Sentry receives from 100% of prod since Sep 22; automation excluded and tagged. Read with docs/load-attempt-telemetry.md's SQL. Caveat: a missing terminal stage is "unknown", not a crash, and stacks are minified until source maps ship.
Consent defaults differ between prod and staging
The prod GTM container's CookieYes template has all six consent signals defaulted to granted for all regions; staging's table is empty so the template denies by default. On playmusic.gg, GA4 gets storage before any banner interaction; staging is opt-in. Flagged, unchanged — an owner decision, not a code fix. Partner embeds run GA4 with cookies and no consent mode; ruled as designed (the partner owns the consent relationship).
Correction to the Sep 10 read-out
The hp-vs-lp label was six campaigns, not one A/B — three search, three YouTube Demand Gen — and YouTube DG was 91% of tagged sessions with almost no human signal. Any homepage-vs-feed comparison has to drop it first; pooled, it made the homepage look ahead. Search alone is too small to call in either direction. Re-read is in progress; the Sep 10 tab's per-channel conversion rates stand, its landing-page rows do not.
/me 500 on every genuinely new sign-in for seven weeks. Hotfixed on prod Sep 15. Every metric on users.is_guest since then has been wrong, and last week's "0 sign-ups" from the Ads test was this, not the ads. Alongside it, Experiment 1 is fully instrumented on prod (song deep-links into the Wizard, 50/50 split short-links, one creation funnel for Wizard and Alchemy, Ads conversions), /foryou finally has a Create entry, and Telegram had three production releases. Staging is loaded for the next push: Game Lists on /foryou, Alchemy reaching XP parity with the Wizard, worlds cleanup, bulk song upload, share attribution.
⭐ Highlights
Hotfix: new-account creation had been broken since 27 Jul
jsonb_build_array is variadic, so under server-side binding Postgres could not infer parameter $6 and rejected the whole INSERT on /me. OAuth succeeded, SuperTokens created the account, /me 500'd, no users row was written, the client fell back to guest and people retried with another provider. Introduced by #924 on Jul 26; the last non-guest row was 2026-07-27 11:43. 23 real accounts exist in the auth core and are invisible to the app. Fixed in prod (23eb6b02, same as #1407 on staging); verified the INSERT prepares and completes for email and null-email providers. Follow-ups: reconcile the 23 orphaned ids; re-read every sign-up number since 27 Jul.
Experiment 1 ("Create a game with [song]") is instrumented end-to-end on prod
?song=<id> opens Wizard Step 1 with the advertised song selected (b3b6a398); &mode=<id>[&submode=] advances past the game-type step, and a hand pick disarms the auto-advance for good (#1383 + Aikido follow-ups). 50/50 split short-links draw an arm server-side, pin it in a 30-day per-code cookie, append pm_arm, forward the ad's own query, and emit campaign_split_assigned for human deduped clicks only; the CMS Campaign Builder gets an A/B section with per-arm arrivals (6c6f9e46, migration 106). Plain /c/ links now forward gclid and utm_* too (9ec8fc90, 8ebd7aba). One creation-funnel vocabulary — creation_deeplink_arrive → start → step → preview_ready → publish / error / abandon, tagged with tool and arm, arm persisted across the sign-in wall — is fired by both the Wizard and Alchemy, which previously had no tracking at all (e5312ebb). creation_created is the terminal event; creation_publish means listed (6a2612d4). Google Ads receives Game Created and Mode Selected as conversions with the real labels (b48a0f13, 0a05de94). Staging E2E covers deep-link + funnel (7e0e2de1).
/foryou gets a way to Create without leaving the feed
The Ads landing surface is a fixed full-screen pager with no BottomNav, so the only way off it was the back chevron. A slim TikTok-style tab bar now sits over the pager with the four BottomNav destinations and Create promoted to a centre + (cf4ea30d); a new feed_create_click event carries card index and visit depth so we can tell whether the feed produced the creator. The registered XP pill is a real tap target and opens the missions drawer instead of /profile (57185907, a4c0a68c); bottom-nav labels are readable over light content (3fa62bb2).
Telegram mini-app: three production releases
A 32-commit staging→main promote (28ecc73) fixed the "stuck on Finding the Match" chain (leave stranding the next match, engine-spoke gating, deferred picker, engine addressed at contentWindow) and the room-config gameMode bug that made guests refuse the host's room; the same gameMode fix was ported to the TikTok activity. GA4 via gtag.js + LiveOps portal funnel landed, then four analytics defects were fixed in the production funnel (c6d55cd): every prod event was labelled environment=staging (the *.workers.dev hostname matched an includes("dev") test — LiveOps filtered to production showed no Telegram traffic at all); the #tgWebAppData launch fragment with user ids and the signed auth hash was reaching Google Analytics on every hit; scoreless runs dropped game_play_end/song_completed. Two more releases followed: a KV-backed leaderboard fallback with the global board derived from raw scores, and brand-2026 alignment (slate palette, site mark, card design, preview clips). CI: Telegram auto-deploys staging on push, production stays a deliberate dispatch.
Also shipped to prod
Alchemy hardening (#1351–#1364)
White screen from a TDZ in the mode-options callback (#1364); mode cards get thumbnails and only the options that belong to the mode (#1362); Obby was unselectable until a song was picked (#1358); the slot panel confirms a selection, honours skip flags and gets the brand pass (#1359); characters come from the catalog the Wizard reads and FX is hidden on course modes (#1356); the platformer course no longer rebuilds on every parent render (#1351).
Run-end and retention on mobile
Play again is a reachable target on mobile and stands the run-end UI down when pressed (1d48e720, c68164d8); the progression flag resolves when the guest read times out, and the test checks the flag field rather than the config object (92e1fe5d, f8f96209); modal analytics lifecycle and teardown delivery hotfixed (18976f16).
Prod plumbing that was quietly failing
CSP now allows CookieYes hosts alongside Cookiebot — the prod cookie banner had been blocked since the migration (f0221895). The CDN answered 206 to plain GETs on /playmusic-pipeline/*, so card-image generation rejected its own reference frames (aafb02cb). Prod portal-cms was missing MEDIA_SERVICE_URL and GAME_PIPELINE_URL, so screenshot capture was unavailable (14c99e92). The Drops Doppler overlay was repointing consumer storage — prod NoSuchBucket (3e3ff5a7).
Smaller
Asset Library gains display-name editing (090e3db4), rename returns 404 for an unknown id (#1368). Dead Discord invite in header and footer and the "Music Music Games" typo hotfixed (fff5f8cd). Survival's env picker ignored its own exclusions (b668fe31). Engine main: the clicker/merge Play card vanished one frame after the mode showed it (2d222c65); no lane-switch line on a side-camera platformer (engine #487).
On staging — queued for the next release
Game Lists — CMS-managed discovery feeds, now on /foryou
Editors build named lists (manual picks or rules over game genre / music genre / artist / song / game id) with a default sort and the filters players may change; resolved client-side with zero per-filter requests (60170e67). Built on the existing generator as a new wizard_config type game_feeds (D1 edge, Promote flow) rather than a new R2 pipeline; D1 0025 adds song_genre to projects. Hard rule: a game with no preview clip is never a list member. Product moved the player surface from /browse to /foryou and restored /browse byte-for-byte (05a4eaff): with no visible list the feed is exactly the previous one; with ≥1 list a second chrome row (list ▾ · Filters · sort ▾ · count) drives the pager and the view lives in the URL.
Alchemy reaches parity with the Wizard (#1399–#1428)
A game made in Alchemy now earns what one made in the Wizard — 150 XP, missions ticked, celebration toast, first-creation mark for guests (#1426). Publish is gated, the game screen's chrome is restored and Shuffle can no longer pick art that does not exist (#1422); the preview stops before Publish creates the game (#1423) and the published game has a way out (#1424). Mobile picker takeover, song-art parity, slot labels and publish identity (#1411); the world picker offers what the Wizard offers and a world you're given is one you can pick (#1427); the preview names the mode it is previewing (#1428); the engine's menu is hidden while building, play is full-screen, mobile gets an exit (#1406); tutorial held, auto-picks shown, finished game marked (b50421d9).
Worlds cleanup (#1429–#1433)
Skyfall is Sky Rider's world only; Boujee Drift is not a world and leaves the pickers; Boujee Night is withheld from modes that render a character; Skyfall, Backrooms and Boujee Night tiles resolve to new art. The Wizard decides the world step from the mode definition rather than mode-id lists (7dfc932c) and shows a World confirmation tile only where a world was applied silently.
Songs: PlayMusic Approved flag + drag-and-drop bulk upload
Informational "PlayMusic Approved" flag (migration 108) on single and bulk forms; new POST /api/tracks/batch-upload takes multiple MP3s with per-file error isolation, then a per-file table → shared fields → cost estimate → generation → live batch status (291e1dc4). Fixed along the way: the "artist required" check ran before ID3 extraction, so a blank artist could never be backfilled from the file. Per-song metadata is editable after a bulk upload.
Share attribution — shares were invisible, not absent
Every share copied a raw /play/<id> URL and never called the share API: share_links last gained a row on 15 Jan, and all 22 September share events carried short_code = null, so a recipient looked exactly like direct traffic. The resolver was fine (verified on prod: 307 with ?ref=, bad codes handled, click counted); only the caller was missing. Tracked short links are now minted on menu-open (a1747d16).
Also on staging
Portal config serialises each game's multiplayer flag (ccb373e2); GTM event-trigger regex widened to feed/identity/retention/session events (ea0a5dd5); migration 107 wired into both runners; wizard-config sync accepts game_type_order so it writes anything at all (b3e95314). Engine: Vector Beat Arena's loudest section stopped spawning, plus a song indicator (engine #500); wingsuit lean HUD by default with configurable off-beat wording; coaster honours resultsMode 'continue'; shooter boss gets real HP and opening spawn HP is capped to what the player can fire; a dead rig is caught instead of rendering a frozen character, with a script to detect skin weights that tear the mesh; audio-flyer values chords like Music Runner, draws the whole chart, and a colour collect is a collect not a detonation; MODE_CAPS coverage pin 24 (engine #481). Integrations: TikTok deploy falls back to staging not production; the MP lobby reads the engine's lobbyState; guest toast wording matches the host side.
Next 7 days
Launch Experiments 1 and 2
Per the eight-week plan: "Create a game with [song]" → Wizard Step 1 vs Alchemy through one split link (~$500 / $500), and "Play rhythm games" → one curated page (~$1,000). Verify ad → /c/ → correct song → creation → publish, and ad → page → play, with attribution intact, before releasing full spend. The curated rhythm page is what Game Lists is for — it needs the next release, or a feed_order-pinned interim.
Staging → prod release
Game Lists on /foryou, Alchemy #1399–#1428, worlds cleanup, bulk song upload, share attribution, GTM triggers; migrations 107 and 108 plus D1 0025. Then an engine promote for wingsuit / coaster / shooter / audio-flyer / VBA and a wizard_config promote behind it.
Reconcile and re-baseline accounts
Fold the 23 orphaned SuperTokens ids into users; re-read every sign-up and is_guest figure since 27 Jul; restart the sign-up series from Sep 15.
Telegram
Deliberate Deploy Telegram production dispatch for anything still on staging; confirm prod events now carry environment=production and that no tgWebAppData reaches GA.
🔍 Monitoring
Sign-ups: series invalid 27 Jul → 15 Sep
0 non-guest users were created in that window because creation was broken, not because nobody tried — 23 accounts made it into the auth core. Last week's Ads read-out ("0 sign-ups, 5 guests") cannot be read as an ads result. Re-baseline from Sep 15; treat any account-based KPI in the Aug 6 → Sep 10 tabs as unmeasured.
Shares: 22 events in September, 0 attributable
Instrumentation gap (no short code minted), fixed on staging only. Keep "share → play" at n/a until the release ships; do not read the September zero as "sharing doesn't work".
Telegram traffic was hidden from the production LiveOps view
Until c6d55cd every prod Telegram event was labelled staging. Any Telegram number filtered to production before Sep 16 is zero by mislabelling; re-pull after the fix.
Experiment 1 readiness
Arm assignment is server-side per short code with a 30-day cookie and a forged-pm_arm guard, so the pill-vs-missions failure mode (no config key, split drifting 24–97% hourly) does not apply. Both tools fire the same funnel. Still to confirm before spend: client_fp populated on prod rows (the Sep 3–7 blind spot — no fix for it appears in this week's history), and that the split link survives the sign-in wall end-to-end on prod, not just staging E2E.
🎯 Inside the loop — the work that raises K
| # | Initiative | Band | Status | ETA | Deliverable | Supporting PRs / evidence |
|---|---|---|---|---|---|---|
| 1 | Progression + live systems — player level, economy, onboarding, mission system, creator conversion, tournaments (incl. further iteration) | Loop · Retention | Shipped; iterating | — | Shipped surface | #981–#1006 (D0–D1 retention, 11 PRs) · #1227 retention on landing feeds · #1350 guest identity self-heal |
| 2 | Game templates — Dance, Zombie Drive, Merge, Platformer (8 PoCs held as option value) | Loop · Retention | September | Sep | Shipped surface | #1276 Beat Dance → main (#1208–#1220) · #1335 Rollercoaster & Sky Rider creatable · #1346 True Platformer course · Merge: no engine template yet |
| 3 | playmusic.gg UX — visitor → player conversion | Loop · Retention | Always on | — | Shipped surface | #1260 mobile LP release · #1282 / #1284 /foryou observer fixes · #1350 guest half-state |
| 4 | Front page curation, chart testing | Loop · Retention | End September | End Sep | — | #1229 CMS feed order (default sort + pinned) · chart testing: no PR yet |
| 5 | Wizard improvement — multi-character & object expansion | Loop · Creator | Dev Complete | — | Shipped surface | #934 / #942 CMS asset slots · #926 extra slots · #1248 entity-slot seed · #1352 override whitelist |
| 6 | Creation flow UX improvements | Loop · Creator | Always on | — | Shipped surface | #1054 wizard port · create-success screen · #1332 Beat Dance preview fix |
| 7a | Creator flow alternatives — Alchemy | Loop · Creator | Early October | Early Oct | — | #1325 live (gated) · #1337–#1349 editor (13 PRs: CMS controls, slots, themes, publish payload, camera/FX, song sections, True Platformer) · #1351 course rebuild fix |
| 7b | Creator flow alternatives — Director | Loop · Creator | Not started | — | — | No PRs |
| 27 | Top 100 + Top 50 global trending track support | Loop · Catalogue | Shipped; ongoing ingestion | — | Shipped surface — ongoing ingestion | #648 catalog CDN URLs · #894 song-asset thumbnails · asset-ingest job (backfill_has_thumbnail) |
📣 Visitor acquisition — feeds the loop, doesn't raise K
| # | Initiative | Band | Status | ETA | Deliverable | Supporting PRs / evidence |
|---|---|---|---|---|---|---|
| 8 | Google paid search tests | Acquisition | In flight | — | — | #1074 Google Ads id · hotfix ads-tag-in-html · #1286 CSP GA4/Ads pixels · gtm-ads-conversion-tags · Sep 3–7 HP vs LP test (see Week of Sep 10) |
| 9 | Telegram mini-app + TG Ads test | Acquisition | Early September | Early Sep | — | No PRs in platform repo |
| 10 | Performance ads — TikTok, Meta, Google | Acquisition | End August | End Aug | — | fix/csp-ads-conversions · #1286 CSP audience pixels · campaign-session-attribution |
| 11 | Artist contests (≥2) — Warren Zeiders 8/28, 9/16 | Acquisition | Dated | 8/28 · 9/16 | — | #923 contests promote-to-prod · promote-contests-promos |
| 12 | Playgama paid UA test | Acquisition | Test | — | — | #1206 portal config self-resolve · campaign paused 19 Jul |
| 13 | Influencer alternatives — wavelink.co | Acquisition | Test | — | — | No PRs |
| 14 | Astrocade paid-per-play tests | Acquisition | Test | — | — | No PRs |
| 15 | Distribution-led growth — PlayHop, CrazyGames, Yandex (incl. Artist Games) | Acquisition | Ongoing | — | Durable placement | PlayHop SDK live (migration 087) · portal-discord-toggle · FRVR embeds live |
| 16 | LG TV app | Acquisition | Shipped | — | Shipped surface | — |
| 17 | Poki | Acquisition | Trial | — | — | No PRs |
🎼 Prosumer / supply — proves or kills “content supply is the constraint”
| # | Initiative | Band | Status | ETA | Deliverable | Supporting PRs / evidence |
|---|---|---|---|---|---|---|
| 18 | Prosumer offering launch — DIY / indie demand test | Supply | End August | End Aug | — | #955 base tier · #963 isolation · #1183 drops.game play modal · #1199–#1205 CMS/roles · #1309 / #1312 / #1328 / #1329 fixes |
| 19 | Clipping campaign UA (ugc.ninja) | Supply | September | Sep | — | No PRs |
| 20 | Warren Zeiders games | Supply | Content | — | Content | — |
📐 Measurement — hidden but essential
| # | Initiative | Band | Status | ETA | Deliverable | Supporting PRs / evidence |
|---|---|---|---|---|---|---|
| 21 | QA for analytics events | Measurement | Not done | — | Gating dependency | #1299 tracking backlog (14 items, staging) · 124d4822 feed-depth tracking · client_fp still empty in prod |
| 22 | Funnel measurement | Measurement | Not done | — | Gating dependency | #794 / #810 / #822 campaign tracking · campaign-session-attribution · portal vs game identity not joinable |
| 23 | CMS — publishing-rights support, reporting | Measurement | Not done | — | Gating dependency — also gates #27 | No PRs |
💡 Proposed — unfunded
| # | Initiative | Band | Status | ETA | Deliverable | Supporting PRs / evidence |
|---|---|---|---|---|---|---|
| 24 | Auto-clip → 9:16 → "Enter this world" end card | Proposed | Unfunded | — | Shipped surface — only organic acquisition in the list | — |
| 25 | Varied worlds bound to play behaviour (combinatorial) | Proposed | Unfunded | — | Shipped surface | — |
| 26 | Remix via parent id + attribution | Proposed | Unfunded | — | Shipped surface | — |
⭐ Highlights
Production release #1325 (Sep 9) — pipeline scale, catalog security, guest retention
Staged 7 days of work to prod: pipeline asset-ingest scaling, catalog visibility hardening (auth + search perms), Alchemy brought into the live creator flow (feature-gated, safe to iterate), guest identity self-healing (#1350) so half-states don't mint duplicate guests, and analytics event QA (tracking backlog cleared).
Rollercoaster & Sky Rider become first-class game types
Both modes unlocked in the creator wizard as standalone game types, not variants. Engine now supplies correct defaults per mode; wizard overrides that forced wrong models got reverted. Rollercoaster got dark-mode support (#1335). Sky Rider dark-mode variant now available too.
Alchemy editor completed across 13 PRs
Shipped in pieces Sep 3–10: CMS controls for Alchemy modes (#1337), asset slots UI (#1342), fill-the-blank mode messages (#1344), True Platformer course (#1346), theme toggle (#1348), publish payload (#1347), song sections UI (#1343), camera & FX controls (#1339), world pins & player slot (#1338), and environment names (#1349). Platformer reference-error fixed (#1341). Isometric view dropped from wizard config (#1340).
Guest identity self-healing (retention win)
Laggy publishes + dead previews + creator flips left guests with session cookies but missing localStorage tokens, triggering the create gate to mint a second guest identity on next visit. Now detects and merges half-states into one identity (#1350). Tests locked in.
Beat Dance preview two-character body fixed
Preview showed engine's runner character + selected dancer simultaneously. Scoped runner suppression to real games only; song preview now correct from start. Dance mode-init now seeds the selected character before apply, so preview and game show the same model (#1332).
🔍 Monitoring
Search converts 59× better than YouTube (but tracking gaps inflated both numbers)
Search: 64 of 770 sessions started a game (8.31%, 95% CI 6.6%–10.5%). YouTube: 11 of 7,756 (0.14%, 95% CI 0.08%–0.25%). The gap is real (p < 0.0001). But YouTube's tagged-session count is ~11× too high because the app's browser wipes its memory each session, so every load looks like a new person; search inflated less. YouTube's 0.58% "human activity rate" (tap, scroll, or play) vs search's 14% is the finding: YouTube delivered almost no people, not YouTube people didn't play.
Landing-page question (homepage vs /foryou feed) unanswered
Search showed feed → 9.4% conversion vs homepage → 6.9%, but needed ~900 tagged sessions per page to tell if real; test had 469 feed, 291 homepage. YouTube showed 0.16% feed vs 0.08% homepage, also too small. Statistically, both differences are noise (p > 0.23). Takeaway: need ~1,800 total search sessions per test repeat to answer this.
Pill vs missions A/B test didn't run
Configuration key to randomise pill vs missions display never shipped to production. Split swung 24%–97% hour-by-hour (a real coin flip would stay ~50%), so no experiment took place. Pill rates: search 10.5%, YouTube 0.22%. Missions: search 8.3%, YouTube 0.08%. Differences are not trustworthy.
Sign-ups and game creation stayed flat
0 sign-ups (5 accounts created were all guest). 7 games created (0 published). 19 of 361 games were played at least once. Campaign generated 7–10× traffic increase but no creator lift.
Key unknowns: device fingerprinting, landing-page tagging
Device fingerprint (client_fp) was empty on every production record during the test—the one field that would answer "is YouTube real people or app-auto-fetches" outright. Tagging was inferred after the fact by landing-page assignment; a proper tag in the ad link would settle homepage vs feed. Both fixes are done on staging and ready to ship. Next test should run with both live.
⭐ Highlights
Production release — three weeks live on playmusic.gg
Everything from Jul 12→21 — SEO/LLM discovery, the performance pass, portal LiveOps & retention, catalog thumbnails, PlayHop — reached production in one staging→main push (#947). It bundled an auth reconciliation: a migration (094) that backfills linked account IDs and merges the duplicate accounts that formed when the same person signed in across staging and prod.
The creator wizard becomes a game-mode toolbox
Horror Survival is now its own mode (with a Horror Runner submode), Spy Hunter joined as a vehicular-combat play style, the shooter gained UA Mode and Zen Flow styles, and Merge became a selectable type. New environments (boujee night/drift, backrooms) and skydomes surfaced too. Creators now pick from a genuine menu of genres instead of a single runner.
CMS-defined asset slots per game type
The CMS now decides which asset slots — traffic, enemies, obstacles, collectibles, characters, environments — a given game type exposes (#934, #942), and players can opt into extra slots (#926). Content, not code, shapes what each mode lets you customize.
Wizard-config snapshot / restore + auto-sync
Wizard configuration now syncs additively from code on every deploy (#931) and can be snapshotted and restored from D1 (#932), so a bad edit or a missed seed can no longer silently wipe modes or environments.
Also shipped
Home create surface polish
featuredCreate modes moved onto the Home page (out of the /create picker) with a readable "New" badge, and the create banner now sits under the Featured Drop hero.
Contests & Promotions promote-to-prod
Contests and promo campaigns can now be promoted staging→prod through the same lineage-tracked flow as games (#923); contest migrations wired into both runners.
Creation funnel visibility
New LiveOps view into the create→publish funnel plus per-song completion attribution (#937).
Play counts stop jumping
The homepage/modal play count now reads a live D1 edge counter (#941), so it no longer flips between a stale replica and the live number.
Next 7 days
R&D / PoC
⭐ Highlights
PlayHop distribution live
PlayMusic games now run inside Yandex's PlayHop portal via its SDK (#825) — a third external surface alongside Discord and TikTok — including the Yandex SDK integration and its auth/player quirks.
SEO/LLM discovery pages
The discovery layer went from hubs to a real graph: directory pages (#849), entity-graph IDs linking songs/artists/creators/games (#848), LLM-oriented hub enrichment (#850), an is-indexable gate wired via migration (#839), and a sitemap/robots audit (#834) that cleaned up what search engines and AI crawlers see.
Performance pass — bundle + images
The main JS bundle dropped from ~809 kB to ~625 kB (SuperTokens UI out of boot, path-based chunking) (#871), the mobile LCP chain was broken up with hero preload (#870), and wizard/catalog images now route through CDN resize — roughly 11–20× smaller (#917). Noticeably faster first load, especially on mobile.
Cross-environment auth hardening
Signing in with a second provider now merges into the existing account instead of creating a duplicate (#886); the OAuth connect flow was locked to same-origin with CSRF nonces (#885); stale cross-env session cookies get cleared (#879); and cached API responses vary on Origin to stop cross-origin CORS bleed (#877).
Also shipped
Contests feature
A Contest Hub with a homepage entry point, landing pages, and CMS authoring (#914), a brand-styled leaderboard with podium + player position (#915), and contest-rules links in promo popups (#913).
Draft-first player games
Player-created games now start as drafts; publishing is a PlayMusic-gated step (#916), plus a post-run "Create your own game" prompt on playmusic.gg.
Portal LiveOps analytics
Retention split by acquisition source × destination — playmusic.gg vs portals side-by-side (#861, #863) — and play-count consistency fixes (#857).
Engine — new play styles
A full shooter pass (walls, power-ups, boss, HP display, weapons), a config-bootable Horror Survival, a Spy Hunter vehicular-combat mode, and a new 2D platformer package with double-jump, gravity-flip, and on-beat scoring.
Next 7 days
R&D / PoC
⭐ Highlights
Create → Publish shipped to production
The new two-step draft→publish flow went live (#732, #738): players create a draft, then publish as a distinct, gated step. This was the release's highest-risk item and cleared prod after a staging publish-route fix.
Catalog thumbnails end-to-end
Song/character and asset thumbnails now render across the CMS asset library and game cards, backed by a has_thumbnail flag (#718), ingest-time rendering (#796), and cache-busting on rotation — no more initials-only placeholders.
Playgama ads hardened
Ads now pause the whole game (not just audio) on overlay (PG-35/37), respect multiplayer eligibility (PG-39), carry legal/noindex correctness (PG-44), and route share correctly on TikTok/Discord (PG-40) — the fixes that make monetized play feel intentional rather than jarring.
Faster embeds
Embedded games got edge-cached config (#788), engine preloading (#789), and deep-link slug resolution (#790), cutting the time from click to playable when a game is embedded elsewhere.
Also shipped
Search & Social report live
The Soundcharts + Google Trends CMS report (admin-only) went to production with its migration and scheduler.
Engine — tutorials & first shooter
A full audit/rewrite of every game and sub-mode's instruction copy (#310), plus the first UA-Mode shooter with a generic projectile/weapon pipeline (Pistol).
Engine — flight & platformer fixes
Per-mode flyer orientation, dynamic floor, collision anchoring, and a single config-driven platformer health/lives model.
SEO & social plumbing
Organization sameAs socials (#807), a Discord social footer (#805), and robots rules that disallow internal routes (#809).
Next 7 days
R&D / PoC
⭐ Highlights
Gameplay-video sharing suite
Players can record a run and share it with rich social links (#696), a video library (#697), and an in-player share (#698) — turning a play session into shareable content across social surfaces.
Personalized home
My Games and Followed Creators shelves now inject over the CMS home layout (#687), plus a "create your own" card (#684) and the brand-2026 polish — Featured Drop, posters, badges (#702).
Content takedowns + enforcement
A global takedown system that hides taken-down content everywhere and blocks republish, with coverage logging (#659, #661, #675) — the compliance backbone for a UGC catalog.
Engine — Alchemy asset picker & Match3
The Alchemy slot panel became a "fill-the-blank" UX driven by live catalog data (#280), a Match3 mode family shipped with four forks, and a SIDEKICK avatar builder PoC (customize + animate) opened up.
Also shipped
Search & Social report + Director NLE
First cut of the Soundcharts / Google-Trends CMS report (#703) and a Director prompt/NLE mode with fuller mode coverage (#704).
Creator wizard batch (#706–#717)
Multiplayer defaults on in-card, unpublished-song characters surfaced, flight preview live-swap, model-less song handling, and a raft of scroll/preview fixes.
Share Hub / Invite Players
A refreshed share hub with an Invite Players component and QR codes (#719).
Ingest & catalog correctness
Vehicle category (#713), idempotent incremental sync (#721), and is_public preserved so CMS "hide" toggles survive re-ingests (#725).
Next 7 days
R&D / PoC
⭐ Highlights
Prosumer — first end-to-end vertical slice is live
The independent-artist self-serve track went from architecture doc to running code: a full slice spanning the API, a dedicated frontend, database migration, and a staging deploy (#455). We then worked through its first real-world bugs — sign-in was 500-ing and song upload was 502-ing, both traced to the frontend's own nginx proxy (SuperTokens headers not forwarded; API resolver + Host header; a reserved PORT env blocking the gateway route). Artists can now stand up on the prosumer frontend on staging.
SEO/LLM — full indexing + landing-page builder (P1–P5)
Last week's AEO foundation grew into the real thing: SEO/LLM indexing with a block-based CMS landing-page builder, image/video content blocks with upload, and the ability to link songs/artists on a game page and manage Song/Artist hubs in the CMS. Migrations wired, human-preview sign-in gate fixed. This is the machinery that turns curated song × genre combos into publishable, discoverable pages.
TikTok Mini Game — Discord parity + multiplayer (on staging)
A third distribution surface came together this week: the TikTok Mini Game landed on staging with full feature parity to the Discord Activity — games browser, scores, XP, song progress, likes/achievements, game-proxy, and multiplayer signaling — plus a two-tab dev harness so multiplayer can be exercised without TikTok partner access (one browser acts as streamer + viewer against the real engine). A cross-distribution platform-profiles framework was also drafted, with TikTok as profile #1. Open PRs to staging (integrations #16, PMH #585), not yet merged to production.
Preview capture — real gameplay, finally
The preview-video pipeline landed on the approach that actually works: capture real gameplay via page screenshots at speed, starting games with a DOM click (Playwright's normal click was hanging on Cloud Run) instead of frozen frames or engine-side exports. Preview videos now show the game genuinely being played — the missing piece for rich, trustworthy game cards.
Also shipped
Home deep-link control
A global home-deep-link default plus a per-game tri-state override in the CMS — fine-grained control over whether a home-screen click routes straight into a game or to its detail page.
Genre & Song/Artist hubs
Game pages now link their song and artist; hubs show all categories with a published-fallback; game-page hub links wired end-to-end — part of the same discovery push as SEO.
Asset rotation & CDN correctness
Model load + rotation fixes across multi-storage backends, credential handling, and a CDN purge on rotation so the immutable-cached viewer reloads the new model instead of a stale one.
Wizard camera & game-mode fixes
Camera reset-to-standard and the top/flight/obby view fixes (partial APPLY_CONFIG no longer stomps a picked Top variant); flight generated-ship-on-floor and drop/obby fixes in the engine; a 4-player game-over guard and leaver-forfeit result for multiplayer.
Next 7 days
R&D / PoC
⭐ Highlights
Discord multiplayer — reconnection fixed + single-socket gateway
Discord Activity multiplayer had two structural problems, both now fixed. Rooms died on an idle drop — redundant reconnect attempts were tearing down healthy connections and never recovering, so you had to relaunch the activity. And you couldn't start a fresh lobby after a match — Discord only allows one live connection per activity, so opening a second one just hung.
The fix is a new single-socket gateway: one persistent connection for the whole session, owned by the outer activity frame (which survives Discord's menu navigation), with rooms switched over control messages instead of new connections. It's behind a flag with a one-line rollback. We also tidied the lobby/spectator experience: watch-instead choice on invites, switching to spectator mid-match, spectators see the results screen, and a host "force-start" to unblock stuck lobbies.
Playgama PoC — rewarded ads through the vendor layer
We wired Playgama in as a first-class ad vendor with a new ad manager that routes rewarded-ad requests with CMS-driven vendor selection — proving the end-to-end monetisation path on a partner channel. Sits on top of last fortnight's session-tracking fixes, so Playgama sessions now report clean length data alongside the ad path. The vendor abstraction means the next partner plugs in without a rewrite.
SEO / LLM discovery pages — AEO foundation (in flight)
A new workstream to make curated games discoverable by search engines and LLMs: llms.txt, server-rendered game-detail pages with structured data (JSON-LD), AI-bot crawl rules, and per-game SEO overrides in the CMS. The model is deliberately curated, not automatic — we hand-pick the song × genre combinations worth owning, gated by a single editorial toggle that defaults to no-index. Architecture is merged; the first feature slice (P1a) is on a branch, not yet to production.
Also shipped
Game previews — big push
Manual preview-video upload and preview controls in the CMS Game Library, hover-to-play preview videos on the consumer feed, and a "has preview video" filter. Capture got much more robust across every mode — dismissing the start screen reliably, capturing actual gameplay rather than the menu, and not mistaking rhythm-mode audio for "song complete." Plus an in-engine 3D model preview in the CMS that shows real in-game orientation.
Wizard & vehicle-picker fixes
Camera perspective only resets on an actual game-type change; the mode accordion centers when it opens; the character/vehicle picker got a right-edge fade so hidden cars are discoverable; going Back from Step 3 keeps your picked vehicle; song-generated vehicles now reuse the catalog thumbnail.
Multiplayer correctness — drift & flight
Resolved the phantom 3rd car in drift and the oversized opponent ghost; fixed flight-ghost orientation, grid offset, flicker and glide smoothness — all derived locally with zero extra bandwidth. Drift traffic is now deterministic across screens (host-seeded, fixed-step). Results screens support 3–4 players and abandoned matches resolve to the survivor.
Skydomes & camera (preview parity)
Picked skydomes now render correctly in the wizard preview across aimer and obby — resilient texture loading with retry and CDN fallback, fixed an upside-down skydome bug, and the camera re-applies on perspective changes. Also: the aimer wizard preview now autoplays a muted demo so you can see real gameplay before generating.
Relay repo split complete
The multiplayer relay is now its own repository with CI/CD at parity with the engine — branch-driven deploys, secrets via Doppler, version drift-checking, and contributor docs. The relay side of the single-socket gateway shipped here too.
Next 7 days
Why it matters
Discord MP feels reliable
The failure modes that made multiplayer feel broken — dead rooms after a drop, can't-start-a-new-lobby — are gone. That's the difference between a demo and something players will use.
Revenue loop proven
Rewarded ads running through a real vendor on Playgama is the first end-to-end proof of monetisation on a partner channel — and the vendor layer makes the next partner cheap.
New acquisition channel
SEO/LLM pages open organic search and AI-answer discovery. Curated-only keeps quality high — every indexed page is an intentional choice, not thin auto-generated content.
Previews drive clicks
Hover-play previews and robust capture mean every game card can show real gameplay — the single biggest lever on whether a browsing player presses play.
⭐ Highlights
Auth account-linking — true merge + reconciliation
Signing in with a second provider now resolves to your existing account instead of creating a duplicate (#886), backed by a new user_identities model and a backfill (migrations 091–094) that merges accounts already duplicated across staging and prod. The OAuth connect flow was locked down — same-origin, CSRF nonce, no unauth fallback (#885). This was the release's highest-risk item: it was reverted once on Jul 21 (#896) and re-landed after hardening.
Creator wizard — new game modes & CMS asset slots
Horror Survival (now its own mode) plus a Horror Runner submode, UA Mode & Zen Flow shooter styles, Spy Hunter on Drift Drive, and new environments (boujee night/drift, backrooms) + YSTO skydomes + a Merge type. The CMS now defines which asset slots each game type exposes (#934, #942), players can add extra slots (#926), and settings show/hide via CMS-authored rules (#944).
Contests
A Contest Hub with a homepage entry point, landing pages, and CMS authoring (#914), a brand-styled leaderboard with podium + player position (#915), contest-rules links in promo popups (#913), and Contests + Promotions added to Promote-to-Prod (#923). Migrations 0011/0012 (Postgres) + 0024 (D1).
Wizard-config safety net
Wizard configuration now syncs additively from code on every deploy (#931), with no-store reads and D1 snapshot/restore workflows (#932) — a bad edit or missed seed can no longer silently wipe modes or environments in production.
Also shipped
Draft-first player games
Player-created games now start as drafts; publishing is a PlayMusic-gated step (#916), plus a post-run "Create your own game" prompt on playmusic.gg (#908).
Front-end performance pass
Main JS bundle cut from ~809 kB to ~625 kB (#882), the mobile LCP chain broken up, wizard/catalog images routed through CDN resize (~11–20× smaller) (#917), and a boot-smoke gate to catch dead bundles.
LiveOps & play counts
Creation-funnel visibility + per-song completion attribution (#937), and the D1 edge play_count kept live so homepage/modal counts stop jumping (#941, #907).
Home / modal polish & worker CSP
Featured Drop banner placement with readable "New" badges, aspect-safe hero media, share unblocked on desktop/iframe surfaces (#905), and engine-iframe frame-ancestors + CORS fixes (#900–#903, #877).
Post-release watch
Requested
Requested
- New Game Modes — Platformer, Dance Game
- Customer Avatar — Beta
- Single-Screen Creator — Alchemy
- Mission / XP System
- Telegram — Multiplayer
- TikTok — Multiplayer
Requested
- First deployment on infra
- First UI/UX pass
Planned
Planned
- New Game Modes — Platformer, Dance Game
- Mission / XP System
- Telegram — Multiplayer
- TikTok — Multiplayer
Planned
- First deployment on infra
- First UI/UX pass
Requested, not in this release
- Customer Avatar — Beta
- Single-Screen Creator — Alchemy
docs/RELEASE_SCHEDULE.md (PR #951) · last reviewed 2026-07-16.
Tier summary
| Tier | Cadence | Lead time | Deploy path | Approval | Rollback target |
|---|---|---|---|---|---|
| Minor | Ad-hoc (continuous) | None | Auto CI/CD → staging → prod | Automated gates | Revert PR / prev revision |
| Standard | Every 2 weeks | Cut at train | Scheduled train | 1 reviewer + preflight | Previous train tag |
| Special | Ad-hoc | Min. 2 weeks | Scheduled train | Stakeholder + eng lead | Flag off / previous train |
| Major | Quarterly (or as scoped) | 4+ weeks | Dedicated cutover | Eng lead + product sign-off | Blue/green traffic shift |
The four tiers
Low-risk, reversible changes that ship the moment they're merged. Push to staging → auto-deploy; merge to main → blue/green auto-deploy.
Includes
- Bugs — defect / regression / crash fixes
- Experiments — flagged A/B tests, toggled trials
- Data — catalog backfills, thumbnail regens, D1 edge re-syncs
- Copy & config — text, CMS content, SEO settings
- Hotfixes — urgent prod-down fixes
Rules
- Behind a flag if user-visible and not fully verified
- Ships continuously — no batching (that's Standard)
- D1 migrations ship with the merge — never defer
Accumulated non-urgent work, batched, soaked on staging, and promoted on the train date. Predictable, reviewable, communicable.
Includes
- Content — new songs / characters, curated catalog changes
- Templates — via engine + playmusic.gg updates
- Minor features — small, self-contained additions
- UI polish + routine, non-breaking dependency bumps
Rules
- Train cut every other week; code freeze the day before
- 1 reviewer + green preflight (unit + cross-browser E2E)
- Not merged by freeze → rides the next train
Time-sensitive, externally-coordinated releases tied to a date or partner. Ad-hoc timing, but never a surprise — they need lead time for assets, legal, and comms.
Includes
- Artist promos — sponsored drops, timed features, pop-ups
- Marketing / campaign launches — landing pages, seasonal events
- Partner integrations — TikTok, LG webOS, etc. with a launch date
- Licensed content windows — contractual go-live / takedown
Rules
- Minimum 2 weeks' notice — inside 2 weeks is best-effort
- Coordination checklist: assets, rights/legal, comms, sign-off
- Prefer a scheduled reveal (flag / publish gate) over a timed deploy
Large, higher-risk changes: new capabilities, architecture shifts, or anything needing a migration or coordinated multi-service cutover. Dedicated planning and a cutover runbook.
Includes
- New features — net-new surfaces (new game modes, engine features)
- Platform — infra, performance, scalability, new deploy targets
- Schema / migrations — Postgres + D1, data-model changes
- Breaking changes + new third-party / SDK integrations
Rules
- 4+ weeks lead; scoped spec + cutover runbook
- Eng-lead + product sign-off; blue/green + canary + manual gate
- Migrations in order: PG → merge → D1 + worker → Sync to Edge
- Feature-flagged where possible so launch ≠ deploy
Classifying a change
Three delivery streams
Scope
- Main application
- Native embeds — Discord, TikTok, (Reddit or Telegram)
- Portals
Scope
- Infrastructure
- New UI
- CDN
Scope
- Mix & match templates
- Mix & match game mechanics
PlayMusic Launch Scope
Modes × 8
- Music Runner
- Drift
- Music Aimer
- Shooter*
- Obby
- Clicker
- Flight
- Platform Runner
Depth
- Sub-modes × 20+
- Environments × 13*
- SFX — Beat Reactivity* (charts & visuals, filters)
Multiplayer
- 1v1
- 2v2*
- Watcher*
Assets — 1.4k
- Generated*
- Library assets*
Game creation
- Game Wizard + Advanced
Game cards
- File upload
- In-game capture
- AI generation
- Preview video*
Embedded channels
- Discord
- TikTok
- Reddit?
- Telegram?
Partner portals
- Y8
- PlayGama
- FRVR
- PlayHop
- CrazyGames (disable leaderboards)
Auth
- Discord
- TikTok*
- Email (?)
Social
- Share game
- Share video
SEO
- Game indexing for SEO / LLM (HTML indexing)
Analytics & growth
- Validate onsite happy-conversion analytics
- Promo — competition cards
- Misc — unknown-user high-score name capture
PlayMusic R&D
Music Mixing
Deeper interactive control over the stems / mix as a play mechanic.
Open World
Exploration beyond the fixed-track loop.
Platformers
Mario / Crash Bandicoot-style platforming.
2D Games
Find, match, and other casual 2D formats.
PlayMusic — Product Strategy
Can't find the right game? Build it in seconds.
Strategy shift. Work is now organised into three parallel streams rather than the prior single-track distribution/content/technology framing. Each stream owns its own surface, audience, and pace.
PlayMusic — main app
The core consumer application plus native embeds (Discord, TikTok, Reddit/Telegram) and partner Portals. The flagship "play & create" loop.
Prosumer — independent artist
A self-serve track for independent artists: dedicated infrastructure, a new UI, and CDN. Lets artists stand up their own music-game experiences.
Decomposing templates
Mix & match templates and mix & match game mechanics — breaking modes into reusable, recombinable parts so new games assemble from a shared kit.
This baselines and iterates on: engagement habits (music & game affinity, sessions, session length), channel effectiveness (impressions, sessions), sentiment (D1/D7/D14 retention around creation & gameplay), and ops model at scale (deployments, games, portals, uptime).
Iteration Matrix
| Learning goal | Distribution | Content | Technology |
|---|---|---|---|
| Engagement habits | Multiple channels = real behavioural patterns | ~10 modes × ~20 environments = enough surface to see what sticks | Live analytics captures it cleanly |
| Channel effectiveness | Y8 + Playgama + Discord gives genuine comparison | Same content across channels = clean A/B | Graduated rollout lets us test without risk |
| Sentiment (creation & gameplay) | Channel mix surfaces different audience reactions | Diverse modes isolate creation vs. play-loop signal | Song-to-game pipeline is the thing being judged |
| Ops model | More partners = more ops surface to stress-test | More content = more throughput demands on pipeline | CMS + LiveOps + deploy panel — we'll see where they break |
Production Status
Technology
- Song-to-game pipeline live
- Engine in production, embedded across internal and external channels
- CMS with full pipeline control, asset management, and deploy panel
- Graduated deployments for maximum uptime
- Google, Discord, and TikTok sign-in
- Director Studio live — NLE timeline + AI cue generation, mid-song mode switching
- Full-funnel analytics + UTM/geo attribution; LiveOps model in place
- Quick user generation
Game Content
- 8 game modes: Music Runner, Drift, Aimer (OSU/Taiko/Mania/Catch), Shooter, Obby, Clicker, Flight, Platform Runner
- 20+ sub-modes; Drift Runner + Crash Course + Horror Survival presets
- 13+ environments live
- 4-player multiplayer (mid-join + spectators) merged to staging
Distribution
- Fully remote control of content globally
- Discord Activity live; partner portals (Y8, PlayGama, FRVR, PlayHop, CrazyGames)
- Campaign builder + UTM short-link shortener
Technology
- Three delivery streams — PlayMusic (main app), Prosumer (independent artist: infra + new UI + CDN), Decomposing Templates (mix-&-match templates + mechanics, Lego-core)
- Multiplayer hardening across all modes (2v2, Watcher)
- Beat-reactivity SFX (charts & visuals, filters)
Game Content
- Shooter mode finalisation
- 1.4k asset library (generated + library assets)
- Preview videos for game cards
Distribution
- Native embeds: TikTok, plus Reddit / Telegram under evaluation
- Game indexing for SEO / LLM (HTML indexing)
- Improved global load times
Technology — platform maturity
- Multiplayer hardening — scale to 2–4 person rooms across all modes (foundation already on
feature/multiplayer; Mid-May ships Music Runner + Duet, Q2 expands the rest) - Tournaments & competitions engine
- CRM in place — closes the loop between analytics and lifecycle communication
- Advanced game-generation flow
Game Content — 10 modes, double today's depth
- 3 new modes shipped (10 in total)
- Multiplayer enabled across all modes (H2H + co-op)
- Tournament / competition formats
Distribution — 4 partner channels live
- Partner-curated content
- Channel-specific competitions and content
Value Proposition & Consumer Messaging
Play-first, create-on-the-gap. The voice makes playing feel effortless and creating feel like the natural next click — never a chore.
Source: PlayMusicHack/docs/TONE_OF_VOICE.md + playmusic-value-prop.md.
The voice, in one line
The two-verb spine
Press play. Can't find it? Make it. — Press vs make: playing is the default, making is the fallback. Keep that contrast intact wherever both actions appear.
Reference lines (calibration set — pass all eight personas)
• Play games to the music you love.
• Press play. Can't find it? Make it in under a minute.
• Your music. Any game.
The test: if a new line would make the Explorer, the Curator, or the Philosopher wince, rewrite it.
Five rules
1 · Plain over hype
"Play games to the music you love" — not "Unleash the ULTIMATE music gaming experience." Plainness reads as confidence to older personas, clarity to younger.
2 · Show, don't sell
Let the song titles, genres, and the actual game be exciting. The copy's job is to get out of the way.
3 · You-and-yours, never "users"
"the music you love," "your game," "the ones you can't find."
4 · Verbs, not adjectives
Play. Make. Press play. Action language is age-neutral; adjective-piles read as try-hard to anyone 15+.
5 · One light human beat, max
"Can't find it? Make it." is playful without cringe. Never two jokes in a row, never stacked exclamation marks.
Who we're talking to
| Persona | Age | Alienated by… |
|---|---|---|
| The Explorer / The Storyteller | 10–12 | Cynicism, complexity, anything like homework |
| The Identity Seeker | 13–15 | Being talked down to, fake hype, "fellow kids" voice |
| The Squad | 13–15 | Boring, corporate, anything not fun or shareable |
| The Curator | 15–17 | Anything algorithmic, mass-market, or try-hard |
| The Main Character | 15–17 | Flatness — they want stakes, drama, a moment |
| The Creator | 17–20 | Being treated as a passive consumer; over-polish |
| The Philosopher | 17–20 | Hype, exclamation marks, shallowness, marketing-speak |
The two poles: young end (10–14) needs clear, warm, energetic, zero-cynicism; older end (15–20) needs understated, authentic, never try-hard — hype is the #1 alienator. What they share: don't be fake. That's the seam the voice threads.
Do / Don't
| Do | Don't |
|---|---|
| Play games to the music you love | Unleash the ULTIMATE music-gaming experience!! |
| Press play. | Drop in. (ambiguous — say the real action) |
| Can't find it? Make it in under a minute. | Become a CREATOR and build your dream game today!!! |
| Your music. Any game. | A platform for music-driven gaming experiences |
| Make your own in under a minute. | Our revolutionary next-gen pipeline lets you… |
| Pick a song, press play. | Get ready to embark on an epic musical journey |
Hard no's (the alienators): stacked !! → loses 15–20 · slang / "fellow kids" → loses everyone · "revolutionary / ultimate / next-gen / unleash / epic" → loses Curator + Philosopher · corporate flatness ("a platform for…") → loses 10–15 · talking down → loses 13+.
Homepage & SEO templates
gameTypeDefinitions)Tier 1 — Homepage (broad, hand-written)
H1: Play games to the music you love
Subhead: Any song, any genre — from flappy flight to endless runners. Can't find your combo? Make it in under a minute.
CTA: Press play · Can't find it? Make it →
LLM-quotable definition (the line an LLM quotes back)
PlayMusic lets you play games set to any song or artist — across genres like flappy, runner, drift, obby, and rhythm — and create your own in under a minute when the combo you want doesn't exist.
Tier 2 — Genre /play/{genre} · Tier 3 — Song /play/song/{artist}-{title}
Both fill {variables} from the same wizardMeta.gameTypeDefinitions the wizard renders from, plus the song catalog. Add a genre in the CMS → its landing page exists with correct copy, no second edit. SEO surfaces lean plainer still — shaped like the query, genre/artist leads the line.
Positioning — the two-tier business value prop
playmusic-value-prop.md · the why behind the consumer voiceValue
- Turn your music into playable, shareable games — not another link fans scroll past
- "Play my song" vs "Listen to my song"
- 5 general OR 2 in-depth generations/yr · AI beat/energy/mood sync · web-ready export
- Minutes to a game vs $50K–$500K / months for custom builds
Market
- 50M+ music creators (SoundCloud 40M+, Spotify indie 11–12M, bedroom producers 20–50M)
Value
- Fan Portal — white-label destination where fans build games with the artist's music
- 10–20× cheaper than custom Fortnite/Roblox builds; scalable across a roster; measurable
- UGC flywheel: activate → fans create → compete → winning game ported to a major platform
Pricing
- $15K/track activation · +$25K custom template · $25K/platform port
- Blended margin 65–80% · Enterprise TAM $500M–$1B
One-liner: PlayMusic — turn music into games. $30/year for indie artists to create; $15K+ per track for labels to activate their fanbase.
What shipped this week
We can finally see load times
Until this week, partner complaints about "45-second load times" were a guessing game — we had no data. The game now reports back exactly how long each step took (initial download, configuration, game assets, audio) for every play session, on every partner.
New "Portals" dashboard in CMS
Anyone in the business can now open the CMS, go to LiveOps → Portals, and see per-partner load times, trends over time, and how many players are coming back to replay. Sparklines on each partner card give an at-a-glance health check.
Drop-out tracking
We also capture when a user bails before the game finishes loading — historically these players were invisible. Now we know how many we're losing and where.
Smart asset loading
The game now adapts how aggressively it downloads assets based on the player's connection and device. Fast connections on good devices get more in parallel; slow phones get protected from getting overwhelmed. This was originally scheduled later in the roadmap but came along for free.
Hover-to-prefetch on portals
When a player hovers over a game card for 200ms, we start loading the game in the background. By the time they click, it's already partway loaded — feels instant.
Discord embeds report in
Discord-embedded games now route through the same analytics pipeline as everywhere else, so we'll see Discord performance alongside Playgama, Y8, etc.
What we still need to fix on this
The new load-time tracking works inside our own site (playmusic.gg). It doesn't yet report from games embedded directly on partner sites like Playgama or Y8, or from our portal shells. So tomorrow's prod dashboard will show consumer-site data first; partner-embed coverage is the next follow-up.
Next 7 days
Why it matters
Data-driven optimisation
Once prod has 7 days of real traffic, we'll have concrete numbers per partner, country, and device tier — and that tells us which speed investment to make next instead of guessing.
Partner conversations
Playgama, Y8, and Discord ask "how fast does it load in our region?" — we can now answer with numbers, not promises.
Drop-off recovery
Drop-out tracking turns invisible churn into a measurable metric. We can fix what we can see.
Free wins along the way
Smart asset loading + hover-prefetch came along with the instrumentation work — already shaving seconds off perceived load.
For our partners (Playgama, Y8, Discord)
Y8 partner attribution fixed
Y8 traffic wasn't being properly credited in our funnel — every session looked like it came from "unknown". Fixed: Y8 sessions now show up correctly in partner reporting, so we can have a real conversation with them about volume and payouts.
Discord Activity foundation
The "game-inside-Discord" launch surface is now mostly in place — Discord Activity UI redesigned, share + mobile UX polished, analytics flowing through the same pipeline as our other partners. Featured Artist Experience landed with STYNGR admin controls for curating which artist gets the prime slot.
Playgama feedback addressed
Items Playgama flagged during partner review have been closed out and shipped to prod.
One-click "promote to prod" for partner content
Previously, getting a new song or partner-portal update from staging to production required manual coordination across systems. Now it's one click from cms-staging — for the team running partner relationships, this is daily friction gone.
For the creator + content teams
New game mode: OSU (rhythm tap)
Full rhythm-tap mode landed in production, with a polished wizard flow for picking it during game creation. Expands the catalogue beyond runner/drift/flight into the rhythm-game category.
Wizard rebuilt around real data
The "create a game" wizard used to have a lot of hardcoded options. It's now driven by actual config — so when content/engineering add a mode, it shows up in the wizard automatically. Less drift, less "why isn't X showing up?".
CMS song delete + maintenance polish
CMS got a clean song-delete flow (previously a support ticket) and maintenance-page polish so partner-visible downtime looks professional.
For the ops / LiveOps team
LiveOps got drill-down views, music data quality, and trend visibility
Five separate LiveOps improvements shipped in two weeks. The internal ops tool keeps getting tighter — drill down from a top-line metric into the underlying sessions, see music data quality at a glance, catch errors that used to go silent. Less time spent asking engineers "can you look this up for me?".
Better partner reporting infrastructure
D1 + Worker Redis runbooks finalised; observability fixes shipped. When something looks weird in the LiveOps dashboard, we now have actual playbooks for triaging it instead of inventing one each time.
For end-users (players)
Two same-day production hotfixes
A bug made collectibles invisible in some game modes (May 4); fixed same day. A separate bug caused phantom "MISS" popups in Cyber Run / Cosmo Run (May 5); also fixed same day. Both were caught before they hit large player numbers — operational maturity getting better.
Flight-mode ship revive UX
Smoother revive flow when players crash in flight mode — small UX nicety, but the kind of polish that compounds.
Mobile UI polish on Discord/partner shells
Mobile drawers, auto-hiding bars, share-link flow refined — the embedded-game experience feels closer to a native app.
Behind the scenes — platform reliability
Production load tolerance improved
Database connection pooling (PgBouncer) extended to production, memory increased on the busiest services, CPU throttling tuned. Net result: we can take more concurrent players without falling over, and OOM crashes that used to happen during traffic spikes are gone.
Engine release process formalised
The game engine now has a proper "canary cutover" promotion system — new versions go to a small slice of traffic first, with one-click flip to full traffic once we're confident. Riskiest part of any release (game engine itself) is now the safest.
By the numbers
15+
Production releases in 13 days. Daily-ish cadence — partners and internal teams get value continuously, not in big quarterly drops.
Zero major outages
Two hotfixes were caught and fixed same-day. No prolonged production downtime in the window.
3 partner channels improved
Playgama, Y8, and Discord all got dedicated work this fortnight — diversifying our distribution risk.
1 new game mode live
OSU rhythm-tap shipped end-to-end — pipeline, wizard, and gameplay. Net new genre in the catalogue.
Engine — features & templates
playmusic-game-engine · new modes, Director Studio, multiplayer, template kitDirector Studio — full NLE timeline + AI cue generation
The engine-side of Director Studio matured into a real non-linear editor: multi-lane timeline, drag-edit cues, scrubbable ruler (SEEK_TO_TIME to scrub the running game), and seamless live mode-switching (APPLY_MODE) with stem crossfade between levels. Switch game mode / map / VFX / song / camera mid-song without a restart.
New & expanded game modes
Drift Runner (music runner with a steerable car), Crash Course (grouped as a Drift sub-mode), the Aimer family (OSU / Taiko / Mania / Catch) with configurable Builder/Breaker stem game-over and Drop integration + fever danger, Obby multiplayer, and a Horror Survival preset (backrooms popout zombies). FlyTap collectibles made reachable; Shooter HUD + enemy preload fixed.
Multiplayer — 4-player, mid-join, spectators
Engine threaded explicit 4-player + spectator caps, a mid-game join flow, and ghost fixes across Drift/Flight/Obby (orientation, phantom-vehicle dedup, ghost height/animation, input gating). Mid-join spectators now start rendering, not just players.
Template kit — Lego-core + env capability bounds
Phase 1–2 of Lego-core: standalone registries with parity-tested canCombine() and a harness-v2 lego route — the seam for mix-&-match templates and mechanics. Plus an env capability-bounds system, Synty Apocalypse traffic pool, and drift/crash-course driving extended to ground+road environments.
SIDEKICK avatar builder (PoC)
Proved live face/head + hair swap on a shared body via slot-agnostic equipPartGLB, natural arms via A-pose re-bind, and head-only hair binding. Foundation for a customizable character pack.
Alchemy mixer redesign
Slot-panel "fill-the-blank" UX wired to live catalog data, More Songs/Games dropdowns, and stable character alternates.
Integration — features & channels
playmusic-integrations + PMH channel plumbing · Discord, TikTok, partner portalsDiscord Activity — live channel
The Discord Activity surface went from foundation to a working channel: redesigned activity UI with portal data + brand styling, mobile drawers / auto-hiding bars / share-link flow, Featured Artist Experience with STYNGR admin controls, and the game iframe tagged ?portal=discord. Activity analytics now route through the /game-api proxy and carry portal slug + user-agent in the envelope.
TikTok auth + new embedded channels
TikTok added as an auth provider (SuperTokens authorize-URL fix). Embedded-channel scope now spans Discord, TikTok, and partner portals — with Reddit / Telegram flagged as next.
Partner portals — faster, cache-correct, measured
Portal cold-start fix Phase 2 (edge-cache /api/portal/config/* in KV); scoped edge-cache purge on portal create / delete / config-save; FRVR dev harness + reliable FRVR ad triggering with correct revives; CMS "Test Portal" button + automated suite; per-portal Clear Cache. Partner portals: Y8, PlayGama, FRVR, PlayHop, CrazyGames (leaderboards disabled).
Campaign builder + UTM shortener
CMS Campaign Builder with a general /c/ short-link shortener and full UTM ingestion; environment-specific short/destination URLs; gateway routes /api/c/ to player-api for campaign links. Closes the loop from promo card → tracked session.
Attribution survives the whole funnel
First-touch UTM/referrer persisted across OAuth; GA4 ↔ Playgama join keys upgraded; shared pm_session_id/pm_user_id stamped into the GA4 dataLayer; CF geo (CF-IPCountry → X-Country) forwarded end-to-end so analytics country isn't NULL. Abandoned sessions closed via pagehide beacon + hourly reaper.
Platform — CMS, creation & analytics
PlayMusicHack · wizard, LiveOps, deploy, securityGame wizard — accordion picker, persistence, funnel tracking
Accordion-style game-mode picker; persistent wizard state so reload/back recall the engine preview in sync; loading screen on re-sync; full end-to-end funnel instrumentation (wizard + Director Studio). Real game-mode labels on Profile + CMS cards (was always "Runner").
Preview videos (P1)
Capture pipeline for hover-preview videos landed — the missing piece for richer game cards (alongside file upload, in-game capture, and AI generation).
CMS — themes, songs, portals
Operator-editable display names for consumer themes; ISRC/UPC/label/parent-label persisted on song upload; per-tab delete for custom portals; cron layer for portal-cms operationalized (never ran in prod before).
Security & reliability hardening
ADMIN auth enforced on CMS portal/game management; SSRF guard on catalog fetch; staging/canary hostnames blocked from indexing; warm instances kept on user-facing services; remaining console.logs DEV-gated. Multiple Aikido review passes closed out.
By the numbers
3 repos, 5 weeks
Engine, integrations, and platform all shipped continuously — features/templates, channels, and CMS/analytics in parallel.
Director Studio live
NLE timeline + AI cue generation — game authoring without code, mid-song mode switching with no restart.
4-player multiplayer
Caps, mid-join, and spectators threaded end-to-end through engine + relay + CMS seed.
New channels
Discord Activity live, TikTok auth in, campaign shortener + full-funnel attribution.
Apr 22-23 · Babylon engine consolidation + AI chart review
Live Babylon engine + per-game config.json everywhere
Finished migrating every game-load path to use the live Babylon engine + per-game config.json. Removed the last legacy "baked HTML export" code paths from the consumer frontend, CMS, and the play API.
Portal import populates engine config
Portal imports now correctly populate game titles and song metadata in the engine config.
AI chart review hardening
Added robustness to the AI-driven chart review pipeline: handles truncated Gemini responses, configurable model, 16K token limit, robust JSON parsing for double-encoded responses.
Apr 29 · Major production push
Portal attribution end-to-end
Every game session played from a partner portal (Playgama, Discord, Y8, etc.) now records which portal it came from. Spans the engine, consumer frontend, and backend; backed by a new game_sessions.portal_id column. Critical for partner reporting and revenue attribution.
Wizard song analytics
When a user picks a song, navigates the wizard, and creates a game, we now track impressions, clicks, and "song builds" — feeding the LiveOps Songs leaderboard for editorial decisions ("which songs should we refresh?").
Share metrics fix
Share funnel had been silently broken since March — frontend was firing one event name and backend was looking for another. Fixed; share metrics will populate from now on.
Tween-friendly share menu
Dropped X/Twitter, added Messages (iMessage/SMS), reordered for tween-relevance. Native Discord share works for connected users.
Rich link previews
Share links pasted into Discord/Slack/iMessage/Twitter now unfurl as rich cards with the game thumbnail, title, and description. Previously rendered as a generic placeholder.
Music integration semantics fix
A pipeline-vs-engine vocabulary mismatch ("drop" / "break" / "breaker" weren't valid engine values) was causing audio to drop out and never recover on some songs. Both the publish path and the engine's runtime are now defensive.
Admin security hardening
All /api/admin/* endpoints now require ADMIN-role authentication at the router level. Removed two shared-secret backdoor endpoints that were guarded only by a hardcoded string in the codebase.
Playgama partner experience
New CDN-cached path for engine assets (immutable headers); portal D1 sync respects curated CMS data instead of overwriting; tab-pause/resume on the game modal; vehicle-URL safety fix to prevent cross-mode contamination.
Quick Builder cross-link
CMS sidebar's Quick Builder now opens the consumer-app /create flow on the matching environment.
Two artist games shipped
Boujee Baby (Kendrick) — Drift mode
New environment (NYC streetscape, taller buildings, highway signs, custom sky), rigged character riding a Taxi Drift vehicle, organic camera pull-back, custom collectibles (flowers + trash), uniform Office wall with 8-color variety, denser lamps, full song-spaced gameplay tuning. Demonstrates the pipeline producing a polished one-of-one experience for a specific artist.
It Takes A Village (Canyon) — Platformer mode
New canyon environment with 3-height collectibles, double-jump + extended hang-time, "no game-over" forgiving mode, rolling wheels, diamond collectibles, song progress bar, always-on tutorial, results-screen polish. Audio unmute fix shipped alongside. Shows platformer mode now ready to host artist content, not just abstract levels.
Why these matter
Both are end-to-end proof that the song → concept art → 3D → game pipeline can deliver branded artist experiences. They also surfaced and forced fixes on real edge cases (vehicle wheel preservation, character swapping, audio fallback, void-damage modes) that now harden the engine for everyone.
Multiplayer — built and gated for testing
feature/multiplayer branch · Mid-May merge targetReal-time relay infrastructure
Cloudflare Workers relay with Durable Objects GameRoom per match. WebSocket hibernation recovery so a worker restart doesn't drop in-flight games. 30Hz tick rate with smooth client-side interpolation between state ticks. Production relay URL wired up in the harness.
H2H (head-to-head)
Full ghost renderer with synced jump, slide, and celebration animations; lane sync; vertical-curve following on hills; postMessage integration with parent frame; dedicated MultiplayerHUD; H2H results screen.
Duet co-op mode
Stem-builder co-op where two players cooperatively trigger stems. Synced stem state, host config sharing on room join, dedicated co-op results screen.
Lobby + spectator system
Lobby with countdown, ready button gated on config load, room-state config persistence in Durable Object storage. Spectator camera + spectator lobby — anyone can watch live multiplayer matches without joining.
Customization
Ghost color picker, opacity slider, emote system, room settings panel, host toggles for ghost/score display. Player avatars with speed multiplier. Auto-removal of stale ghosts.
Currently gated
Modes restricted to Music Runner + Duet during testing. Flight and Drift disabled in MP for now — they'll come online as physics-sync mode matures. Mid-May ships the gated set; Q2 expands across all 10 modes.
Discord Activity — foundation laid
Frame-ancestors + trusted origins for partner embedding
CSP frame-ancestors updated to allow Y8, Playgama, GamePix, and localhost. Portal-shell can be embedded in y8.com iframes without breaking. Same plumbing extends to Discord Activity iframe at launch.
Portal attribution end-to-end
Game sessions now record portal_id at the engine level — Phase 1 (engine sessions) and Phase 2 (frontend + backend regex matching) both shipped. When Discord Activity goes live, it'll plug into the same attribution pipeline with no additional work.
Embed-mode UX
Login button hidden in /e/ embed routes. Recording.js skipped in portal/embed contexts. Tab-pause/resume on the game modal. Cumulatively this is the embed UX Discord Activity will use.
What's left for Mid-May launch
Discord Activity SDK integration, Discord OAuth scopes, lobby/voice channel awareness, and submission to Discord's developer portal. Underlying iframe + attribution + embed UX is already production-tested via Y8 / Playgama traffic.
What broke (and was fixed) during the push
credentials: 'include').
Why it matters
Partner reporting unlocked
We can now answer "how many sessions came from Playgama vs Y8 vs Discord this week?" and start setting payouts/billing on accurate data.
Editorial signal restored
Share metrics + song engagement tracking surface "which songs are popular" — informs content refresh decisions.
First-impression upgrade
Rich link previews when users share games make Tweets, Discord posts, and iMessages noticeably more attractive to click. Compounds organic share growth.
Security posture
Admin endpoints are now properly gated. Two shared-secret backdoors removed.
Outstanding for next week
eng# engine · int# integrations. Previous reconciliation 2026-07-30.
Ranked by ROI — impact ÷ effort
Re-sorted to surface the highest-leverage work first: small efforts with outsized impact at the top, large bets where the impact justifies the spend further down. Effort and impact are subjective 1–5 scores; ROI = Impact × 2 − Effort (so small / high-impact items lead).
Top 10 by ROI
deploy-microservices-production.yml.@sentry/react + error boundaries. We have product analytics (LiveOps) but no error tracking for prod traffic. Blind spots cost minutes during incidents. Grafana stack can come later — Sentry alone delivers 80% of the value.get_read_connection() already exists in game-pipeline + asset-service. Provision Cloud SQL read replica + flip to use it. Largest remaining single perf lever for backend reads. Falls back to primary so it's safe.By pillar — open work breakdown
⚡ Performance
get_read_connection() to provisioned Cloud SQL replica. Falls back to primary safely./api/config/modes edge cache~620ms first-load. Cache at edge.👤 External user / creator
/personas with scope toggles for profile + asset use.cms-nav-restructure-plan.md.video-capture-tiktok-plan.md. Not started.⚙️ Ops
babylon-game-engine, playmusic-game-sdk, r3f-game-engine dirs.PlayMusicHack
~/Projects/PlayMusicHack/docs/backlog/✅ Shipped — Week of Jul 2
TikTok Mini Game (staging / in flight)
- ✅TikTok Mini Game — Discord parity + multiplayer — games browser, scores, XP, likes, game-proxy, MP signaling. Open PR to staging.int#16int#18int@28ecc73int@5b03b9d
- ✅Two-tab dev MP harness — exercise multiplayer without TikTok partner access (streamer + viewer, real engine).ca11f46int#16
- ✅Platform-profiles framework — cross-distribution config; TikTok as profile #1 (docs).#585
Prosumer — independent-artist track
- ✅Prosumer vertical slice — API + frontend + migration + staging deploy (end-to-end).#455
- ✅Prosumer sign-in 500 fixed — forward SuperTokens headers in the frontend /api proxy.#582
- ✅Prosumer song-upload 502 fixed — nginx API proxy resolver + Host header.#581
- ✅Prosumer gateway route unblocked — drop reserved
PORTenv from the frontend deploy.#580
SEO / LLM discovery
- ✅SEO/LLM indexing + landing builder (P1–P5) — block-based CMS landing pages.#545
- ✅Content blocks with upload — image/video blocks in the landing builder.#567
- ✅Song/Artist hubs — link song/artist on game page + manage hubs in CMS.#571
- ✅SEO migrations wired + human-preview gate — portal-cms SEO migrations; sign-in gate fix.#563#570
Preview capture
- ✅Real-gameplay capture — page.screenshot of live play (not record_video / frozen frame).#554
- ✅DOM-click game start — Playwright click hung on Cloud Run; start via DOM click.#557
- ✅Preview endpoint + service routing — correct service, screenshot speed, warmup gating.#561#556
Home deep-link · hubs · assets · wizard
- ✅Home deep-link — global default + per-game tri-state override in CMS.#572#569
- ✅Genre hubs — show all categories, published fallback, game-page hub links.#565#571
- ✅Asset rotation + CDN purge — multi-storage rotation, credentials, immutable-cache reload.#577#584
- ✅Wizard camera + game modes — reset-to-standard, top/flight/obby views, MP 4-player guard + leaver forfeit.#244#246
✅ Shipped — Week of Jun 25
Discord multiplayer — gateway & reconnection
- ✅Reconnection storm fixed — single-flight
connect()stops redundant calls tearing down healthy sockets; idle 1006 drops now recover.5756260b - ✅Single-socket gateway — one persistent WS to the relay's
/gateway/ws, rooms via control frames (fixes Discord's 2nd-WS-per-instance wedge). Flag-gated.d5d5c10da9bbc93 - ✅Activity-owned socket —
ParentBridgeSocketmoves socket ownership to the outer activity frame so it survives Discord menu nav.4afd521d - ✅Gateway correctness — bridge is Discord-only (fixed web regression);
leave-roomscoped to roomId (fast-restart race).a5ffadd79b92110a - ✅Lobby / spectator UX — watch-instead picker, in-place spectator switch, spectator results screen, host force-start.ddc3ffad
Partner / discovery bets
- ✅Playgama rewarded-ads vendor — AdManager routes rewarded requests with CMS-driven vendor selection (PoC).7f07e780
- ✅SEO/LLM — AEO foundation —
llms.txt, edge-render game-detail pages, JSON-LD, per-game CMS SEO (P1a, branch).09bfe9cede84f018
Game previews
- ✅Preview videos end-to-end — manual upload + detail-modal controls, hover-play on consumer feed, "has preview" filter.d259f9bb8f6bd6be
- ✅Capture robustness — adaptive start + deathless capture, gameplay (not menu) on gated modes, audio autoplay fix.bf1e0312b763cbc2
- ✅In-engine 3D model preview in CMS — real in-game orientation.267d4a20
Multiplayer correctness · wizard · skydomes
- ✅Drift phantom 3rd car + oversized ghost — centralized character re-enable; ghost scaled by room mode.fbc3fec946b0b678
- ✅Flight ghost — orientation/pitch/roll, grid offset, flicker + glide; non-default ghost stays upright. Zero extra bandwidth.21d3cd7f8a99a6ed
- ✅Deterministic drift traffic — host-seeded, fixed-step cadence + seeded lane shuffle; matches across screens.0d9aba97
- ✅Wizard UX — picker edge-fade, Back keeps vehicle, accordion centers, song-vehicle thumbnail, camera-reset scoping.13cf2d48e8ec6a88
- ✅Skydome preview parity — resilient load (retry + CDN fallback), un-flipped skydome, aimer/obby preview fixes, aimer autoplay demo.4ed0536b786c1817
- ✅Relay repo split complete — pm-mp-relay standalone with engine-parity CI/CD + Doppler secrets + drift-check.352fe92a
Backend Microservices
Tech debt
- ✅psycopg2 → psycopg3 conversion — ~10 files in player-api + media-service still import psycopg2 at module level. Mechanical 1:1 translation.3ad011ac
- ✅PostgresManager duplicated across 7+ services — each has its own
infra/postgres_manager.pycopy. Move to shared module.c80d147b - ✅WizardPipelineManagerV2 duplicated — exists in portal-cms, game-pipeline, asset-service, celery-worker. Single source of truth needed.46e842a7
- ✅D1 game sync is manual — CMS "Sync to Edge" must be clicked after publish. Should auto-sync.87ff298f
- ✅Docker layer cache serves stale shared code —
backend/shared/changes may not propagate due to GHA cache.8b62bf27 - ✅Game categories inconsistent — DB has old categories (runner, rhythm) for migrated games; consumer app has new (endless, music, drift).8afbf67d
- ✅Monolith deploy fallback — staging workflow silently falls back to dead
playmusic-backend-stagingif a service URL fetch fails. Hard-fail or remove.825e2c2b
Latent bugs (April audit)
- ✅TIME_UPDATE log spam in GameModalV2 — handler added.verified
- ✅start_session non-UUID insert — accepts
"dev-test"as guest_id; psycopg rejects. Validate or 400.#973#1130 - ✅get_leaderboard route ordering — static
/leaderboard/creatorsmatched by parameterized route. Reorder.@fd7c3a6 - 🟡Transient psycopg connection drops — Cloud SQL cuts idle connections; pool doesn't pre-ping.#1024#1127Oct 8: Pool hygiene fixed (idle-in-transaction leak, starvation) and PgBouncer sidecar removed (#1025); the SQLAlchemy engine already had pool_pre_ping=Tr…
- ✅asset-service OOM on 512 MiB — bump to 1Gi or profile leak.cc68a05f
Performance follow-ups (load test 2026-04-21)
- 🟢Read replica wiring —
get_read_connection()exists but isn't called yet. Provision Cloud SQL replica + wire up. - ✅Production deploy parity — pool size, PgBouncer config, max-instances bumps from staging.02204539
- ✅Upstash Redis activation — leaderboard + likes instant reads/writes at scale.6047ef48
- ✅PgBouncer sidecar permanent in staging — load test fixes including DB pool sizing and edge caching.9ceb43b4
- ✅Guest sessions → stateless JWTs — eliminates last Cloud SQL hot-path write for unauthenticated users.a40b98a9
Cold-start load time (2026-05-13)
- ✅Cold-start instrumentation end-to-end — engine reports timing back to host; backend captures per-session step breakdown (download / config / assets / audio); country + device tier dimensions populated.a98263a5af4f019b
- ✅Drop-out tracking — users who bail before session start now counted via beacon ping; race condition vs in-flight session fixed.b6048dae9ac973ad
- ✅Portals dashboard in CMS — LiveOps → Portals tab with per-partner KPI cards, inline sparklines, trends + retention.8a6588403ae7b382
- ✅Hover-to-prefetch on game cards — 200ms intent prefetch on consumer + portal-shell; saved-data / low-tier defended.2e3a8125a8d03cc4
- ✅Per-session perf token — closes Aikido IDOR finding on the new perf endpoint.595b692fba045eff
- 🟡Partner-embed coverage gap — perf reporting wired from consumer site only. Direct Playgama / Y8 iframes and portal shells not yet reporting. Audit each host + decide engine fallback strategy.#1519eng#514#1700#1711Oct 8: Load/crash telemetry now exists for the consumer modal and the engine emits errors to any host, but direct Playgama/Y8 engine iframes and portal sh…
Production cutover
- ✅game-pipeline service extraction — still partial.8b54e940
- ✅engine-api service stabilization — works but flagged unstable.98a2698f
- ✅
/api/games/purge-cacheroute missing on prod gateway — add nginx route.9c97ad4a - ✅Production SuperTokens instance — production still hits staging SuperTokens. Need dedicated instance + DB.8488be22
CMS & Creator Tools
frontend/.Concept art & creative direction
- ✅Concept Art CMS Screen — backend complete (6 DALL-E images, 8 persona variations). Need React screen: gallery, variation cards, selection, reference upload.#1308#1310#1315#1325
- ✅Per-asset upload/replace — replace concept art image in R2 from CMS.0441947a
- ✅Per-asset prompt regen — show full prompt + extra direction field, regenerate single asset via gpt-image-1.193aa754
- ✅Per-asset 3D from concept — Meshy I2D button per concept image.d90cb0f5
Alchemy UI port
- ✅Port Alchemy to React — engine
packages/alchemy/→consumer-frontend/src/pages/Create.jsx. Replace hardcoded defs with catalog API.#1238#1232#1242 - 🟡
/createroute — split-panel: engine iframe left, song/mode/character pickers right. Replaces multi-step wizard.#1238#1604#1606Oct 8: The split-panel editor shipped as /alchemy; /create routes to it only behind the create_split CMS flag, which is OFF in both envs, so /create still…
Share & recording
- 🟢Share Experience plan — full plan in
docs/share-experience-plan.md. Not started. - ✅Recording engine port — Babylon recording.js sourced from
backend/game-server/static/, manually synced. Make engine repo canonical.767690cf - ✅
/play/:gameIdGameModalV2 wrap — currently bare EngineFrame. Should wrap in full GameModalV2 (likes, leaderboard, share).2f705103
Promote to Production
- ✅Promote to Production CMS feature — batch select songs/games/portals from CMS.5cfa3b4f
- ✅Promotion service hardening — engine URL rewrite, project_id relink, cascades, schema fixes.7c5b5ee7
- ✅E2E promote flow — first Playgama portal promote landed (with hotfixes); 7 games healed via idempotent re-run.7c5b5ee7
Other CMS gaps
- 🟡Asset Library Tool — standalone
/asset-library.htmlwith semantic search across 4,000+ catalog assets.#1361#1325Oct 8: The CMS Asset Library page (frontend/src/pages/AssetLibrary.jsx, pgvector semantic search since March) got display-name editing and vector-search v… - ✅CMS nav restructure — plan in
docs/cms-nav-restructure-plan.md.76185308 - ✅Stale chunk error boundary — lazy-loaded routes fail after deploys. Auto-reload on chunk failure.756308a6
- ✅Wizard preview env reset bug — after handleConfirm, mode-init effect re-fires with default
neonArcade. Visual only.fac37fbf
Song-to-Game Pipeline
Per-song asset slots (drives concept art + 3D)
- ✅Configurable slot system — replace hardcoded 9 concept art slots + character/obstacle/collectible workers with per-song JSONB
concept_art_slotsconfig. Each slot:{id, type, label, generate_concept, generate_3d, art_style?, rigged?}.6d87aba0 - ✅Slots panel in CMS — UI to add/remove/rename slots before generation. Defaults to current 9.3856028b
- 🟢Engine config compat — slot ids → engine roles. Decision pending: keep canonical filenames or remap in config builder.
- ✅Migration — new column on
songs, default = current 9-slot config.6d87aba0
Curated art-style dropdown
- ✅Style catalog — ~12 styles vetted against gpt-image-1 + Meshy lowpoly: anime, western cartoon, comic book, Studio Ghibli, cel-shaded, low-poly stylized, claymation, synthwave, noir, watercolor (2D-only), pixel art (2D-only).0af52a90
- ✅Per-song default + per-slot override — song profile gets
art_stylefield. Regen modal exposes per-asset override.6d87aba0 - ✅Prompt injection — fragments slot into existing prompt builders.46e842a7
- 🟢Style verification batch — generation run across all 12 to confirm Meshy-safe flag is correct.
Personas viewer (standalone CMS page)
- ✅
/personaspage — list all 8 hardcoded personas with name, age range, description, system prompt. Read-only v1.45ba0509 - ✅Two scope toggles per persona — "Use for song profiles" + "Use for asset generation".658945d9
- ✅Persistence decision pending — move 8 personas into
personastable, or keep hardcoded + side table for scope toggles.mig 053/054
Multiplayer
- ✅Real-time multiplayer foundation — Cloudflare Workers relay, Durable Objects GameRoom, WebSocket hibernation recovery, 30Hz tick.c2f3f806d442cc
- ✅H2H (head-to-head) gameplay — ghost renderer, lobby, postMessage integration, MultiplayerHUD, results screen.abb0361661c802
- ✅Duet co-op mode — stem builder co-op with synced state and dedicated results screen.bd94019a7dd79f
- ✅Spectator camera + lobby — watch live multiplayer matches.cfd2d697f2ce35
- ✅Multiplayer customization — ghost color, opacity, emotes, room settings, host toggles.24df3d6
- ✅Merge
feature/multiplayerto staging — branch is feature-complete; gated to Music Runner + Duet during testing. Mid-May ship.8d12b671 - ✅Expand multiplayer to all modes — flight + drift currently disabled in MP; Q2 target enables across all 10 modes.2e10fd77
- 🟢Score challenges (async ghost share) — share link "beat my 12,000" using existing ghost replay infra.
Tournaments
- 🔵Tournament definition — schema needed: tournament, entry, leaderboard scope, prize, time window. New CMS page to manage.
- 🔵Time-bounded leaderboard scope — D1 leaderboard infra exists; needs tournament_id filter.
- 🔵Tournament UI on consumer — entry, current standings, countdown, results.
- 🔵Reward / prize plumbing — out of scope until tournament v1 ships.
Deeper sharing
- 🟢Score-share with replay link — share link includes recorded session for ghost playback.
- 🟢Highlight clip share — auto-generate clip of best moment for TikTok/Discord.
- 🟢Challenge invite — share-with-target to specific friend, results aggregated server-side.
- 🟢Embed share image — OG image with score, song, character thumbnail. Pre-rendered server-side.
Integrations
TikTok
- 🟡Profile "Connect" gating — not gated on
VITE_ENABLE_TIKTOK_SIGNIN. Auth sign-in is gated; profile connect button always shows.#1160Oct 8: The guest sign-in surface on /profile is now gated on VITE_ENABLE_TIKTOK_SIGNIN, but the signed-in account 'Connect TikTok' block in Profile.jsx (t… - 🟢TikTok video capture flow — full plan in
docs/video-capture-tiktok-plan.md. Not started.
Discord
- ✅Discord Activity (game-in-Discord) — full plan in
docs/discord-activity-integration.md. Not started.3a28557 - ✅Discord activity analytics via gateway proxy — analytics calls now route through
/game-apiso Discord-embedded games are visible in the same pipeline as Playgama / Y8.d7817b6
CrazyGames
- 🟢CrazyGames SDK integration — slash command exists for export prep. Not yet wired into publish flow. Submit at least one game.
Native app (Capacitor)
- 🟢Phase 0 (1-2 wk): PWA basics — manifest, service worker, installable.
- 🟢Phase 1 (4-6 wk): Capacitor shell, Apple + Google Sign-In, store submission.
- 🟢Phase 2 (4-6 wk): Push notifications, offline caching (~500MB), native share, score challenges.
- 🟢Phase 3 (8-12 wk): IAP, ghost replays, Babylon Native prototype.
Apple Sign-In + Luminate
- 🟢Apple Sign-In frontend wiring — backend already configured. Required for App Store submission.
- ✅Luminate music data — backlog in
docs/luminate-backlog.md. Decide before scoping.c2fc013d
Infrastructure
Deploy
- ✅Deploy panel + Force Deploy + maintenance page — non-dev control panel with branded maintenance/404 toggle.b1fff58dde771703
- ✅Canary preview routing + fallback — auto-set cookie + 404 fallback to stable.98d679c4c04801ad
- ✅Graduated rollout (production) — production canary live; backends graduate to 100% before gateway+frontend.441aaaa0
- 🔵Blue/green deployment — ~$320/mo extra. Revisit if canary doesn't stabilize.
D1 / edge
- ✅Auto-sync D1 on cache purge — published games sync to D1 edge automatically.9a619716
- 🟢Asset catalog prod sync — staging has 4,628 assets, production has 1,428. Bulk D1 sync.
- ✅Thumbnail URL cleanup — production games reference staging API URLs. Migrate to CDN.2953fa38
- ✅
/api/config/modesedge cache — ~620ms first load. Cache at edge.2024e482
Performance & storage
- ✅Engine iframe load time — engine-side optimizations + immutable CDN headers + redundant audio load skip.ca7058a
- ✅R2 jurisdiction cleanup — switched to default endpoint for WEUR buckets, removed dual-client routing.5921d3b9a9a6a088
- 🔵R2 multi-region — all WEUR today. US/APAC for latency.
- ✅Thumbnail storage unification — pipeline bucket → assets bucket + CDN URLs in D1.1649bccc
- ✅Read replica provisioning — Cloud SQL replica + wire
get_read_connection().7fb7c3ac
Observability
- ✅Sentry on consumer-frontend — error boundaries, error tracking.#1519#1524#1529#1683
- 🟢Prometheus + Grafana Cloud — unified dashboards.
- ✅LiveOps on production — hybrid PostgreSQL + BigQuery analytics live, dynamic source badges, Creation tab.4e6e5d0ea0b9fcf7
- ✅Migration 042 (analytics_events) — ran on prod via 044-048 hotfix workflow.0ba4065d
Other
- ✅Remove Godot prefetch — D1 game feed + project detail return Babylon engine URL; wizard prefetches Babylon engine, not Godot.5b4790442e145c07698c581d
- 🟡Delete husk packages —
babylon-game-engine/,playmusic-game-sdk/,r3f-game-engine/are deprecated.#612Oct 8: Two of three husks were deleted before the window (#612); packages/babylon-game-engine is still on main and CLAUDE.md now calls it active and slate…
Open Bugs (April snapshot)
Production
- ✅TikTok "Connect" shows on production Profile — gating fixed.verified
- ✅Game likes refreshing in GameModalV2 — Upstash Redis + D1 edge writes resolved sync issues.6047ef4803b22e3b
- ✅
/api/games/purge-cacheon prod gateway — D1 sync side-effect cleaned up.38672324
Catalog / preview
- ✅Wizard preview env reset to neonArcade — fixed.verified
- ✅Catalog vehicle/ship thumbnails — generation + upload run, R2 backfilled.verified
- ✅Catalog vehicle orientation auto-correct (engine-side) — runtime fix for Y-up oriented vehicle models.f81195c
- ✅Drift vehicle wheel preservation + swap — VehicleController preserves GLB hierarchy and shared modelCache.098ac55de310b7
Mobile / dev experience
- ✅Director mobile touch input — override select-none for inputs to enable touch.6320356
- ✅TIME_UPDATE log spam — handler added (or throttled engine-side).verified
- ✅Stale chunk errors after deploy — lazy-loaded routes fail. Need React error boundary that auto-reloads.756308a6
Babylon Game Engine
~/Projects/playmusic-game-engine/docs/backlog/Engine Runtime
Performance
- ✅Iframe load time — engine-side optimizations + immutable CDN headers + redundant audio load skip.ca7058a
- ✅Engine debug logging —
[STATE]/[gameLoop]console logs stripped before production.verified - ✅Adaptive asset-load concurrency — tier-aware GLB preload batch size (sequential gating preserved for low-end).03939a14
- ✅Cold-start perf instrumentation — engine reports timing marks to host via PERF_TIMING; Server-Timing parser captures gateway overhead.488154ff62e554c9e2cbac02
- 🟢Bundle size audit — measure current Babylon + game code; tree-shake unused modules.
- 🟡Asset progressive load — load scene before all GLBs are ready, swap in as they arrive.eng#554eng#558#1699int#42Oct 8: A scene-loading-handoff workstream (engine #554/#558/#563–#567, platform #1699/#1724, integrations #42) shows the menu while models finish and boun…
Audio
- 🟢AAC (.m4a) fallback for Safari — full plan in
AAC_FALLBACK_PLAN.md. ~30 lines new code.
Iframe protocol bugs
- ✅TIME_UPDATE log spam — resolved on PMH side.verified
- ✅SET_VEHICLE_MODEL swap fix — route to DriftMode's VehicleController; reparent meshes correctly.33c980ab617443
- ✅Director mobile touch input — override select-none on inputs.6320356
Retargeting / animations
- ✅Runtime animation retargeting — fallback verified; library skeleton kept alive, minimal retarget options for same-convention skeletons.3a38c5637fbfe5
- ✅Babylon.js v8 upgrade — required for
AnimatorAvatarretargeting (CHR-03, ANIM-02, ANIM-03).1db82d75 - 🔵Babylon.js v9 upgrade — supersedes v8 work. WebGPU defaults, IBL improvements, NME updates. Likely batch with v8 (skip straight to v9).
- 🟢Shared animation library — Quaternius UAL packaged into
anims-core.glb, etc. on R2. Eliminates per-song animation gen.
Catalog assets
- ✅Vehicle orientation auto-correct — runtime fix in engine for Y-up oriented vehicle models.f81195c
- 🟢Engine player model API unification — 3 URL fields + 3 SET actions. Long-term: unify into one.
Engine Tools
Alchemy (creator)
- ✅Port to React in PMH — Alchemy creator rewritten as React, connected to catalog API.verified
- 🟢GLB lazy-load + pagination — character grid currently loads all at once.
Director Studio
- ✅Director Studio UI — full plan in
DIRECTOR_STUDIO_PLAN.md. Standalone page with timeline + cue editor + VFX/audio/scene panels.5ba325fd - ✅Director touch handling — mobile touch input enabled for inputs.6320356
Harness — embedded Scene Editor
- ✅Slot editor + asset browser in harness (v0) — first cut shipped 2026-04-20.1daf5c4
- 🟡Embed full Babylon Scene Editor — beyond slot editor: drag-place props, edit lights/cameras, materials. Save scene back to game config. Likely iframe or panel inside
packages/harness/.eng#543Oct 8: Direction superseded: instead of embedding Babylon's Scene Editor, Forge is getting a visual editing workspace (gameplay/assets/music/presentation… - 🟢Scene → config serialization — Scene Editor writes
.babylon/glTF; serializer maps editor entities → our slot/spawn/entity model. - 🟡Asset library bridge — Scene Editor pulls props/characters from catalog instead of file uploads. Custom asset browser panel.eng#543Oct 8: Catalog-fed asset selection exists in the open Forge workspace PR #543 (and 8353e9b5 'live staging song catalog' on feature branches); nothing on m…
QA Harness, Asset Library, Anim Harness
- ✅QA tools panel — full spec in
QA-TOOLS-PLAN.md. 11 tool categories.146791f0 - 🟢Standalone
/asset-library.html— browse all catalog assets, filter by type/style/pack, semantic search, 3D preview. - 🟢
/anim-harness.html— load any character GLB + animation library, test retargeting. Blocked on Babylon v8/v9.
Husk packages
- 🟢Delete
packages/babylon-game-engine/— deprecated split. - ✅Delete
packages/playmusic-game-sdk/— split to separate repo.#612 - ✅Delete
packages/r3f-game-engine/— deprecated.#612
Engine Bridges
Iframe / postMessage protocol
- 🟡TIME_UPDATE handler / throttle — see runtime section.eng#536Oct 8: likely shipped via eng#536 — scope unverified
- 🟢Bridge API doc —
PlayMusicBridge.tshas 3 player-model URL fields + 3 SET actions. Document and unify.
Recording
- ✅recording.js source-of-truth — currently in PMH, manually synced into engine. Make engine repo canonical.767690cf
Multiplayer & sharing hooks
- ✅Real-time multiplayer surface — Durable Objects WebSocket relay, ghost renderer, lobby, customization.feature/multiplayer
- 🟢Score-challenge async embed — accept
?challenge=<replay_id>, fetch ghost data, render semi-transparent player. Reuses real-time ghost infra. - 🟢Highlight clip detection — engine emits "best moment" event for share clip extraction.
- 🟢Pre-rendered OG image — server-side OG image; engine just provides screenshot endpoint.
Native & CrazyGames
- 🔵Babylon Native viability — currently blocked by no Web Audio API support.
- 🟢CrazyGames SDK ad hooks — engine emits "level start"/"level end"/"rewarded ad opportunity" events.