Forty-Two Twice

We build an isometric game with AI agents and open-source our tools.

Forty-Two Twice is a two-person studio. Our game is set in a Bangkok neighborhood under an elevated railway. We keep structure in data and code, and let image and video models paint on top.

Blocks: a street of the neighborhood as boxes in world units, rendered by IsoBlock Flat render: colors and shapes on the same camera Painted pass A: an image model paints over the blocks Painted pass B: a second paint pass over the same blocks
One street, the same camera. Art style is not final.

The street

The layout comes first. The paint comes last.

Each pass keeps the camera and the positions of the one before. The plan and the blocks are data we can check; image models paint on top of a structure they cannot change.

  1. Floor plan rotated to the camera, with lanes and traffic directions

    Plan

    Real rules become data: traffic keeps left, a 4 m median, a railway 10.4 m above the road.

  2. Blocks render of the street

    Blocks

    Every object is a box in world units, checked before anything is drawn.

  3. Flat render of the street

    Flat render

    Colors and readable shapes on the same camera: the color template the paint must follow.

  4. Painted pass A Painted pass B

    Painted, twice

    Two paint passes keep every object in place, yet change the details and even the style. That drift is why the layout file, not an image, is the source of truth.

Swipe to see every pass

Characters

One master image, many motions.

How our test character went from one drawing to sprites that move, in six steps. She is a stand-in while we build the pipeline; her final look is not decided.

  1. A drawing, then a rig

    We started from both ends: a picture with the right look, and a skeleton that can move.

    The test character drawn by an image model

    Design

    An image model draws her from a written brief. It looks right, but it is one picture: no parts, no second angle.

    The same character drawn entirely in code, with flat shapes

    All in code

    A 15-joint skeleton with IK, and springs for hair and shirt. Every pose is correct, but the surface looks like vector art.

  2. Code as a guide

    Next, the rig guided the image model. Same model, same prompt, two runs each. We measured how closely the result follows the guide's shape, how often the arms stay clear of the body so code can cut them out, and thigh width against height, which is 0.134 in the design.

    Input: a flat color template drawn by codeResult following the color template

    Color template

    • Follows the shape 87–90%
    • Arms clear of the body 53–61%
    • Thigh to height 0.10

    Follows the shape best and keeps the arms clear for cutting, but it also copies the rig's slimmer legs.

    Input: a stick-figure pose with guide linesResult from the stick figure, with the dashed guide lines painted into the picture

    Stick figure

    • Follows the shape 64–67%
    • Arms clear of the body 0–35%
    • Thigh to height 0.14

    Legs come out right, but the arms stick to the body and the dashed guides end up painted into the picture.

    no template
    Result with no template, only the reference image and a written pose

    No template

    • Follows the shape 75%
    • Arms clear of the body 37–41%
    • Thigh to height 0.12

    Closest to the design's look, with no control over the pose.

    What we learned. A guide controls the pose, but the model also copies the rig's proportions, and stick-figure lines leak into the picture. ControlNet would lock the shape, but it needs open models on a GPU of our own; the image APIs we use take reference images and text only. So code stopped drawing guides for her look.

  3. One master, edited kept

    We approved one master image in a neutral pose. Every new image is an edit of that master, never of the previous edit, so small changes cannot pile up.

    The approved master image: the character in a neutral standing pose
    The master, approved once
    A key pose edited from the master: arms up, shirt swinging
    A key pose, edited from it

    What holds, what drifts

    Face and proportions hold. Hair and shirt follow the new pose on their own.

    Small details, like the seams of the shorts, can still change between edits.

    The key poses become the first and last frames for video.

  4. Video between two key poses tested

    Video models fill in the motion between two frames we have already approved: the master as the first frame, a key pose as the last.

    Same first and last frame, three models.
    • Pose change. Seedance 2.0 Fast lands on the last frame with a 95% shape match, feet sliding 1% of her height. Kling 3.0 Std: 94% and 2%. Veo 3.1 Lite ignores the last frame: 64% and 9%.
    • Dance loop. With the first frame repeated as the last, Seedance closes the loop. Veo 3.1 Lite does not come back (76%).
    • 360° turn. One Veo 3.1 Lite clip gives eight matching directions; back, hair, shirt and sunglasses hold.

    First and last frames are the strongest control we have, so the model choice follows the job.

  5. Code keeps the video on model

    Video models drift in color, zoom in a little, slide the feet and repaint what should stay still. Each of those errors is something code can measure and undo.

    Color drift and sliding feet

    Over four seconds the model pulls her colors away from the master and lets her feet wander. Code finds her feet in every frame and pins them to one point. Then it pulls her colors, and the background's, back toward the master.

    The dance loop from Seedance 2.0 Fast.
    16.6 → 3.9color distance from the master, median per frame
    1.3% → 0.8%largest foot slide, as a share of her height

    Color distance from the master, per frame

    Raw videoAfter code
    Color distance from the master, per frameRaw video drifts from about 5 to 16 to 19; after code it stays between 2 and 5.051015200 s1 s2 s3 s4 sRaw videoAfter code
    Show the numbers
    Time (s)Raw videoAfter code
    0.004.681.83
    0.0810.683.53
    0.1716.113.83
    0.2515.853.79
    0.3316.233.97
    0.4218.344.43
    0.5018.684.47
    0.5817.554.33
    0.6717.64.09
    0.7515.993.54
    0.8315.681.99
    0.9212.321.94
    1.0012.471.98
    1.0815.743.93
    1.1716.644.19
    1.2516.524.31
    1.3316.924.25
    1.4218.334.47
    1.5018.674.59
    1.5817.554.36
    1.6717.723.91
    1.7515.834.17
    1.8315.852.01
    1.9211.781.93
    2.0014.491.86
    2.0815.734.07
    2.1716.424.2
    2.2516.394.35
    2.3317.144.47
    2.4217.724.43
    2.5018.274.38
    2.5818.524.68
    2.6717.284.5
    2.7515.92.08
    2.8316.022.15
    2.9212.763.15
    3.0013.093.21
    3.0813.182.1
    3.1713.292.09
    3.2517.254.15
    3.3318.014.44
    3.4218.64.33
    3.5019.034.67
    3.5818.874.66
    3.6718.094.44
    3.7517.014.28
    3.8313.423.95
    3.926.472.04
    4.005.664.16

    A slight zoom, and a background that should stay still

    We asked for leaves, flowers, clouds and water to move, and nothing else. The model also zoomed in 1.7%, shifted the frame by 7 × 9 px and repainted every pixel. The zoom and the shift never change during the clip, so code measures them once and undoes them. Then it finds what really moves, about 15% of the frame, matches its colors to the painting and pastes the original back everywhere else.

    Middle and right: difference from the painting; brighter means further. Tested on an earlier concept of the street.
    • Raw clip 19.4
    • Zoom and shift undone 11.5
    • Still areas pasted back 0.07

    Mean difference from the painting in areas that should not move, per pixel, 0–255.

  6. Sprites

    Frames picked from the fixed clips become sprites. What still needs our eyes: expressions that change on their own, crossed legs, and the gap between the legs when we cut her out.

    Eight-frame dance loop of the character, playing as a sprite

    A dance loop: one beat, 8 frames

    One second of the fixed loop, every third frame.

    The character turning through eight directions

    8 directions from one turn

    One 360° clip, so every angle matches.

Six image models, one set of tasks

Same prompt, references and size for each model, one sample each, so take single results with a grain of salt. No model won every task, so we pick a model per job.

A new master from the design and a written pose. All six look right. None put the head at 10% of the frame as asked, so framing is left to code.

NB2: new master
NB2
GPT Image 2.5: new master
GPT Image 2.5
FLUX.3: new master
FLUX.3
Seedream 5.0 Pro: new master
Seedream 5.0 Pro
Seedream 5.0 Flash: new master
Seedream 5.0 Flash
Qwen Image 3 Pro: new master
Qwen Image 3 Pro

Motion in the game

The street is painted once. Only what moves is animated.

A finished scene is a still picture. People, animals and vehicles are separate sprites on top, moving along lanes and anchors from the scene file. Nothing redraws the street frame by frame.

The painted street
vehicle lanewalkwayanchor
Lanes and anchors from the street's scene file, drawn with its camera over a painted pass.
  1. Plate planned

    The approved painting, plus a clean version without people and vehicles, made as an edit of the same image. Tried on an earlier concept.

  2. Sprites tested

    Separate sprites for characters, animals and vehicles. Tested on our main character; not yet placed in the street.

  3. Placement built

    IsoBlock's Godot adapter puts each moving sprite on the scene's lanes and keeps it in the right draw order.

    Runtime.add_actor(root, "walker", size, start, sprite)
    Runtime.move_actor(root, "walker", next_point)
  4. Light and weather next

    Day to night, rain and steam over the same plate, as shaders and particles in Godot or as relit plates.

Why we built IsoBlock

Agents can't see the screen. Image models can't count.

An agent working in a game engine cannot see what it just did. An image model paints well but places things loosely. IsoBlock gives both of them the scene as data: the agent reads and checks the layout as text and numbers, and the image model gets an exact order of boxes, sizes and anchors.

JobTiledGodot editorAgent in GodotIsoBlock
Lay out a scene before there is artTiles and objects, no rulesEmpty boxes, no rulesA script or a screenshot for every questionAnchors, zones, lanes and relations; checks with measured values
Arrange things once art existsGood, by eyeBest: see it, adjust itWeak: screenshots onlyKeeps the positions; drag in its editor
Draw order around long objectsLeft to the engineY-sort by node origin; long objects sort wrongWrite your ownSlices and sort keys, checked in Godot
Order art from image models———Boxes, block renders, sizes, anchors, states
Into GodotIts exporter skips image-collection tilesetsNativeNativeRuntime file and adapter, within 0.49 px of its renders
Gameplay, animation, light, UI—YesCode yes; feel needs a personNo, by design

Not proven yet. Parts of IsoBlock overlap Godot: its editor, its scene tree, its pathfinding. We have not yet compared IsoBlock with working directly in Godot on a full scene. Its one real use so far caught two layout conflicts before any art, and showed us six gaps to fix.

Open source, MIT license

IsoBlock

A layout tool for 2D isometric scenes. One scene file holds every object's position in world units; people and agents edit the same file, and one approved layout goes to the engine, to image generation and to graybox gameplay.

  • Checks for visibility, clearance, overlaps, lanes, walkable reach, capacity and draw order.
  • Made for agents: a short text summary, small patches that respect locks, variants compared by measured values.
  • Solver that places objects from relations and names the rules in conflict.
  • Outputs: SVG and PNG, a runtime file for engines, boxes for image generation, a Godot adapter.
  • Editor: one offline HTML page with locks and a live check panel.
npm ci && npm run build
node dist/isoblock.mjs check tests/fixtures/yard.scene.json

Spec, source and roadmap on GitHub

IsoBlock sample scene An isometric yard with a tree, a hidden figure, two crates, a bench and two lanes. Marks show the three failed checks. figure 40% hidden crates 0.2 apart lane misses the right edge
  • failc4clearance: crates 0.2 apart, minimum 0.5
  • failc7lane_reaches: haul lane misses the right edge
  • failc8visible: figure 40% hidden by the tree
  • passc1–c3, c5, c6, c9

The sample scene in IsoBlock's tests, with three deliberate mistakes.

Choosing the engine

Godot, after a trial an agent could run.

The target is a native phone app, and the most demanding part of the game is light over painted scenes: 2D lights with normal maps, water, rain, cloud shadows.

Phaser is the easiest engine for agents: TypeScript, runs in a browser, takes its own screenshots. Picking it for that alone would serve the builder, not the game. So we tested whether an agent could run Godot.

Godot 4.7DefoldFlutter + FlamePhaser 4 + Capacitor
Phone appNative exportNative, smallNative Flutter appInside a WebView
2D light with normal mapsBuilt inWrite your ownWrite your ownBuilt in
Web previewHeavyLightNot measuredFastest
Runs in our agents' sessionsYes, with rendersDownloads blockedSDK blockedYes
Scene editorFull: scenes, animation, light, particlesYesNoneNone

Trial E1: can an agent run Godot?

A builder agent loaded IsoBlock's sample scene into Godot 4.7.1. The maintainer agent ran it again in another container and matched every number.

  • 0.49 pxlargest gap from IsoBlock's renders, 5 scenes, 213 objects
  • Identicalrenders, byte for byte, across runs and sessions
  • 0.00046largest error on 13 golden projection vectors
  • 28.3 MBdebug Android build, made without Gradle
  • 10 MBgzipped web engine, single-threaded build
  • 2.3 sone edit, run and compare loop

A 2D light reading a normal map worked too. We chose Godot 4.7 with GDScript. IsoBlock stays outside the engine, and a small GDScript loader reads its files.

Popular routes we skipped

three.js

A 3D rendering library, not an engine: no editor, no scene format, no native phone build. For a painted 2D street on a fixed camera, we would be writing our own engine to get less than Godot.

Blender through MCP

An agent can drive Blender through MCP, and it demos beautifully. On a fixed isometric camera, modeling and rigging every object costs more than it returns. We would open Blender for many angles or true normal maps.

One-prompt games, image-to-3D

Great for a first minute. A game we keep updating needs saves that survive updates, art that stays consistent across hundreds of assets, and scenes we can change without starting over.

How we work

Agents build. We decide.

IsoBlock reached version 0.8 in eight reviewed stages, each one through the same loop.

  1. Spec first

    A maintainer agent (Claude) writes the specification and the expected test results.

  2. Build

    A builder agent (Claude Code) implements one stage on its own branch.

  3. Review

    The maintainer checks the branch's behavior against the expected results.

  4. Merge

    One of us reads the review and merges the stage.

A lesson from going public. Agent tools add links to their sessions in commits and pull requests. We strip them by rule and open pull requests ourselves.

Notes from shipped games

Studios solved several of our problems with structure long before image models. We borrow their ideas, not their budgets.

3D under 2D

With a single artist for its first year, Dead Cells rendered 3D models into pixel-art frames with normal maps, so retakes did not need redrawing by hand.

Game Developer, 2018

Outfits as parts

Spine builds mix-and-match characters from per-item skins at runtime, so animations never depend on one outfit.

Spine User Guide: Skins

Main characters stay hard

Rovio fine-tuned image models on its own art. Season-pass backgrounds went from 20 days to 4, but main characters stayed short of its bar.

Summary of a GDC 2025 talk