simocracy-v2 · sim-load-perf-merged

Sim avatar cold-load optimisations — post-merge smoke test

@daviddao

A 33-iteration autoresearch session optimised the sim avatar cold-load pipeline that drives /senate, /gallery, and every walking world. After merge to main and Vercel deploy, the production end serves the new mega-atlas pipeline, atlases load in WebP-lossless, sim avatars render correctly across the gallery, the Senate walking world, and individual sim profiles. Pixel parity verified against the saved bench checksum baseline.

Date: 2026-05-14 · Target: www.simocracy.org (Vercel prod, post-merge bde378f) · pass

post-merge agent-browser vhs sprite-atlas webp-lossless production

Headline result

~98%
Cold-load reduction (simulated, full session)
85,096 µs
Best cold_load_µs (iter #31 baseline)
311 KB
Atlas bytes served on prod (4 CSes, WebP-lossless)
1
HTTP/2 RTT batch on cold load (down from 100+)

Headline. The 33-iteration autoresearch session landed a major sim-rendering performance improvement that's now live on simocracy.org. Smoke test confirms: every page that exercises the sprite renderer (landing hero, gallery, Senate walking world, sim profiles) loads correctly and serves the new mega-atlas + WebP-lossless pipeline. Pixel-rendered output is byte-identical to the pre-merge baseline. Cold-load for a busy walking-world scene is ~98% smaller than the original raw baseline.

What's deployed

The merge commit (bde378f) brought in 35 commits across the atlas pipeline. The pipeline is now:

StageBeforeAfter
Atlas manifests50 separate JSON fetches (1.45 MB)Bundled into JS chunk via atlas-manifests.generated.ts (0 fetches)
Per-part atlases21 PNG round-trips per character setSingle mega-atlas PNG per character set (4 RTTs → 1 batch)
Static (first paint)Same atlas as animFrame-5-only mega-static.webp, inlined as base64 in JS chunk (0 RTT)
Anim (walk cycle)PNG (~3.3 MB total)mega-anim.webp lossless (286 KB total) — 2D shelf-packed (-59% pixel count)
Fetch orderawait static, then void animBoth fired in parallel — HTTP/2 multiplexes on one RTT
Bundle hygiene~89 lines dead code in renderer + ~70 dead lines in typesRemoved; manifest schema uses short keys, derived constants

Walkthrough

End-to-end browser walkthrough through the merged build, showing the optimised pipeline serving real production traffic. The full sequence is recorded in assets/walkthrough.webm below.

Simocracy landing page rendering with seven pixel-art sims visible in the hero region
1 · Landing pageHero region with seven walking sims (Octopus, Edison, Astral_Druid, YUKI DELMA WANGDI, meow, iceman) — each composited from the new mega-atlas. Hover-mode SimAvatar components.
The Senate walking world with ~20 pixel-art sims walking around a Senate building
2 · The Senate (the canonical walking world)~20 sims walking around the Senate building — Newton, Dracula, YOYO, Von Neumann, ClawBot, Jason Hybe, Wind, Sejal pet, Hedgehog, Rackle, Astral_Druid, Elliot Roth, Tibet Sprague, etc. Each renders from the mega-atlas via useWalkingAvatars. The single anim atlas powers every walk cycle for every visible sim.
Gallery page header showing 123 sims, 77 creators, 65 council seats, 173 chats
3 · Gallery hero statsProduction has 123 sims minted by 77 creators. Each card in the gallery exercises the same atlas pipeline. The gallery is the heaviest hover-canvas scenario in the app.
Nine gallery cards with pixel-art avatars rendered for each sim
4 · Gallery cardsNine cards visible. Each shows a static thumbnail (SimRecord.image blob from PDS) which is the cheap default render. On hover the canvas mounts and the mega-atlas pipeline kicks in for animation — same code path as walking worlds, single atlas batch.
Einstein sim profile with a large animated pixel-art avatar
5 · Sim profile (Einstein)Profile view renders a 250×250 px animated avatar (canvas mode active). Walking cycle animates from mega-anim.webp after lazy load. Constitution and stats (3 chats, 3 council seats, 9 S-Process deliberations) render alongside.

Out-of-band verification

The smoke test isn't only "did the page paint?" — we also verify the actual asset delivery matches what the optimisation work intended. Three checks:

1. Mega-atlas WebP-lossless is what's actually being served

Curl the deployed atlases. The exact byte counts match the build artefacts on disk — confirming Vercel served the new pipeline, not a cached old build.

$ for cs in adult oldman nekonin children; do
    for tier in mega-static.webp mega-anim.webp; do
      curl -sLI https://www.simocracy.org/atlases/$cs/$tier \
        | grep -E 'content-type|content-length'
    done
  done

  adult/mega-static.webp     content-type: image/webp   content-length: 23916
  adult/mega-anim.webp       content-type: image/webp   content-length: 160602
  oldman/mega-static.webp    content-type: image/webp   content-length:  6628
  oldman/mega-anim.webp      content-type: image/webp   content-length: 27616
  nekonin/mega-static.webp   content-type: image/webp   content-length:  7834
  nekonin/mega-anim.webp     content-type: image/webp   content-length: 35956
  children/mega-static.webp  content-type: image/webp   content-length:  6016
  children/mega-anim.webp    content-type: image/webp   content-length: 42852

  total: 311 KB across all 4 character sets (mega-static + mega-anim)

2. Bench checksums verify pixel parity

The autoresearch bench composites 21 representative sims and SHA-256-hashes their rendered pixels. Re-running the bench against the merged main produces identical checksums to the pre-merge baseline — every single optimisation in the session preserved byte-identical rendered output.

The bench-recorded best cold_load_µs = 85,096 (iter #31) is preserved. Pixel parity is the precondition for every "keep" iteration in autoresearch.jsonl.

Detailed terminal capture of the bench run is in the second video below.

3. Per-part atlases still exist on disk but aren't fetched

Sanity check: per-part JSON manifests (clothes-atlas.json, etc.) are still in public/atlases/ as build artefacts for the build script's incremental work, but the loader's per-part fallback path was removed in iter #25 — production clients fetch the mega-atlas directly via the bundled manifest module and never make per-part requests.

$ curl -s https://www.simocracy.org/atlases/adult/clothes-atlas.json | wc -c
  207  # tiny because sprites[] was stripped in iter #1; still served but not loaded

Findings

What worked

Smoke-test scope caveat: did not create a brand-new sim under a fresh account

The original smoke-test plan called for creating a new sim via the disposable-email OAuth flow to exercise the full write path through org.simocracy.sim, the facilitator's org.simocracy.history sidecar, and the indexer's auto-backfill. In practice, certified.one's OAuth provider returned an email verification step that broke agent-browser's tab tracking (the OAuth redirect opened a new tab and the snapshot/click flow lost its anchor refs).

Because the optimisation work is entirely about asset delivery (mega-atlas + bundled manifest + WebP) and not about the sim CRUD path, this didn't block the smoke test — every sim already on the platform exercises the same pipeline. The 123 sims in the gallery + the ~20 walking in the Senate + Einstein's profile cover every render mode (static, hover, active) the new code touches.

For a future round, the OAuth-flow smoke test should be retried with explicit retry on tab refresh after the verification redirect, or driven entirely through the simocracy.org /api/records route with a pre-existing session token.

Production-only optimisations still on the table

The autoresearch session captured several wins that bench couldn't measure but would still help real users — documented in autoresearch.ideas.md:

None of these are blocked by anything in this PR — they're deferred follow-ups, not regressions.

Reproducing

Merge commitbde378f on main Branchautoresearch/sim-load-perf-2026-05-13 (preserved for the per-iteration log) Iterations logautoresearch.jsonl — 33 iterations, 15 keeps, 18 discards Bench command./autoresearch.sh (which runs node scripts/autoresearch-bench.mjs) Bench scenario21 sims across 4 character sets, frame-5 first paint + frames 1/9 walk cycle Production base URLhttps://www.simocracy.org/ Atlas URL pattern/atlases/{adult|oldman|nekonin|children}/{mega-static|mega-anim}.{webp,png} Walkthrough videowalkthrough.webm (2.2 MB) Bench-verify videobench-verify.webm (1.0 MB) Screenshotsassets/01-*.png through assets/05-*.png