The identical metroidvania prompt, run by
Fable 5 · Lumen Vale
and
Opus 4.8 · Sunken Atrium.
Every concrete difference pulled out of the source, side by side. Raw numbers,
no scoring.
Method note. This page lists facts, not verdicts — what each codebase
actually does, so the differences can be read directly instead of through an
Opus-shaped interpretation of "what Fable is doing well." Two would-be
differences were checked and thrown out: both games use the same fixed
1/120 s timestep, and both set imageSmoothingEnabled = true.
The physics constants also can't be compared raw — see §1.
0
What's identical (checked, ruled out)
Before the differences: the two builds share a large, near-parallel
feature set. These were verified the same in both, so they are not where the
divergence lives.
fixed timestep · both 1/120 s accumulatorimageSmoothing · both ONscene machine · title→play→dead→win, bothcoyote time · bothjump buffer · bothvariable jump height · bothdouble-jump gated by pickup · bothAABB axis-separated collision · bothcamera lerp + room clamp · bothi-frame blink · bothknockback on hit · bothscreen shake · bothdamage flash · bothfade transitions · bothsquash & stretch · bothgrounding shadow · bothvignette · bothparallax background · bothambient dust motes · bothHUD hearts + ability slot · bothanimated gate hint · bothanimated exit portal · bothpickup halo + bob · bothchroma-key + despill + auto-trim pipeline · bothprocedural sprite fallbacks · bothe2e debug hook on window · both
A
Architecture at a glance
What came out, the module graphs, and where each model's file-edits
actually landed. Counts verified from source and the build logs (the earlier version of
this block had eyeballed numbers — particles are 16/9 not 22/12, and Opus has more draw
methods, not fewer — corrected here).
Player + Enemy + Game all in one file — physics, camera, draw, gate, HUD
↓ imports ↓
particles.ts 126L
16 emit calls · circles
sfx.ts 71LAUDIO ✓
WebAudio, 7 sounds
assets.ts 210L
idle + run sprites
input.ts 44L
keys
level.ts 41L
room geometry (flat consts)
Opus · 9 files · 1525 LOC
more modules, entities split out
main.ts 36L
boot (leaner)
↓
game.ts 648L
loop, 13 render/draw methods, gate, HUD, screens
↓ depends on ↓
entities.ts 349L
Player + Enemy pulled into their own module (idle-only sprite, no run pose)
config.ts 42L
extracted constants
util.ts 29L
aabb · clamp · approach
particles.ts 136L
9 emit calls · squares
assets.ts 142L
idle sprite only
level.ts 59LAUDIO ✗
typed Level interface · no sfx module exists anywhere
where the edits landed — most-edited files (verified counts)
▮ Fable▮ Opus
game logic
game.ts ×9
entities ×5 + game ×4
test / QA harness
~0 (ran via bash)
game.spec ×6 + qa-shot ×4
level / content
×2
×3
juice (particles)
×2
0 in top edits
config / shell
×5 (vite,style,index,input)
×6 (config,index,main)
Verified side-facts: visual QA was comparable (Fable 16 image-reads / 12 playwright runs ·
Opus 14 / 9). Narration volume was not — Fable 2,313 chars across 9 text blocks, Opus
9,994 across 46 (4×). Opus's single most-edited file was its test
(game.spec.ts, 6 edits); Fable's was its game
(game.ts, 9 edits).
1
Coordinate space & resolution
The single most important fact for reading everything below: the two
games run in different-size pixel worlds. Every constant in §2 lives inside one of
these spaces, so raw number-vs-number comparisons are not apples-to-apples.
Fable · Lumen Vale
internal canvas 960 × 540
room width 1440 (1.5× viewport)
ground line at y = 470
pixel area 518,400 px
Opus · Sunken Atrium
internal canvas 480 × 270
room width 720 (1.5× viewport)
low floor y = 236 · plateau y = 150
pixel area 129,600 px (¼ of Fable)
2
Physics constants
Raw values as written, then a normalized column that removes the
resolution gap (Opus distances ×2 into Fable's 960-wide space, or expressed as a
fraction of each game's own screen). Read the normalized column for feel.
Pulled from the stream-json build logs (tool calls + the model's own
narration) for each run. Facts, with the verbatim lines that source them.
Q1 · why no run sprite?
The decision was made in the asset manifest — a planning artifact
written before any game code — not in the renderer and not during QA.
Fable · planned 2 poses
gen-sprites.ts lists player-idleandplayer-run
run prompt: "mid-stride running pose facing right, cape trailing, one leg forward one back, leaning into the run"
the words run-cycle / walk-cycle / sprite-sheet / animation appear 19× in its log
Opus · planned 1 pose
gen-sprites.mjs lists 6 sprites, player-idle only ("standing idle facing right")
hero framed from the start as "chroma-keyed, mirrored + squash/stretch by code"
those same animation words appear 0× in its entire log — the option was never raised, not raised-and-rejected
Q2 · why the simpler level?
Both did comparable visual QA (Fable 16 image-reads / 12 playwright runs;
Opus 14 / 9). The difference is what each scoped the level as and what its edits
optimized toward.
Fable · built it, barely narrated it
total narration: 2,313 chars / 9 blocks
shipped a 3-step vertical climb of jump-through platforms on the first pass
tuned the jump arc against measured geometry (code comment: "max rise ≈131px → cannot clear the 210px gate")
one design line in the whole log: "got the whole vertical on the first sprite batch"
Opus · scoped it as a mechanic demo
total narration: 9,994 chars / 46 blocks (4× Fable)
scoped the room as "one screen… with a high plateau the player can only mount via double-jump" — one mechanic to show
its level edit optimized for test determinism, verbatim: "a cleaner design: move the enemy's patrol away from the pickup so the player can stand under the wings and jump straight up to grab them" → traversal got simpler
spent QA effort on its own test harness (found "keyboard.press releases instantly, so every jump was getting clipped to a 7px hop") and on confirming gate-blocks / wings-unblock / state: win
Two observations (for you to weigh, not conclusions): (1) the run-sprite gap lived in
a checklist, not in skill — an asset manifest that mandated "idle + run for any
walking character" would close it deterministically. (2) Opus's level bent toward whatever
its success signal was — a green, deterministic e2e — so the layout simplified to make
the grab repeatable. The verification target shaped the artifact.