trompe
Paints a whole gray game level with image models. It plans cameras where a player can stand, paints what each one sees, and bakes one texture your engine lights with a real sun. A believable town takes about 23 minutes and $4.44, on your own image service key.
Good for realistic buildings seen from a few metres or more: third-person and top-down games, trailers, big backdrops, and a first look at a level before you commit to real art. Not for stylised looks: it's a photo painter, so it adds grime and uneven colour. Not for the ground at a first-person player's feet: that blurs, because the whole level shares one texture. Its output fits only the level it was painted on, so it stays in your game and doesn't go in Assets.
The demo replays a real run on the village in samples/: 128 views, $4.44, 23 minutes of image calls. Press Paint it and the whole run replays in 10 seconds, then scrub the views, turn the level and swing the sun. No model runs in the demo, because the game host blocks outside calls.
For your agent
Do these in order, and tell the person the cost before spending anything.
- Check it suits the game. It should be realistic, with the camera a few metres back. If it's first person or flat-colour low poly, say so and suggest something else. Those are what trompe does worst, and the person's the one who'll be looking at the result.
- Set up. Node 22 or later, then:
On Windows it runs through ANGLE's D3D11 backend by itself. If Playwright's browser isn't installed, pointnpm install npx playwright install chromium export FAL_KEY=the-persons-own-key # https://fal.ai/dashboard/keysTROMPE_CHROMEat any Chromium or Chrome. - Prepare the level. A
.glbthat's unwrapped, with Y up. It stops with an error if there are no UVs. Name each part by what it is, through glTF material names ("house walls", "tiled roof", "stone stairs"), because the prompts read them. Blender can do all of this headless. Everything in the level gets sent to the image service, so use only your own levels and CC0 parts, keep CC-BY parts' credits, never send anything taggednoai, and check first before sending store packs or Mixamo files. - Get a free quote. This picks the cameras without calling a model, in about a second:
It prints the number of views. Each costs 1¢ to 4¢ and takes about 10 seconds. Quote a little over: the hole pass adds views while painting, so the village was planned at 122 and came to 128 views, $4.44 and 23 minutes. Say: "That's N views, about $X and Y minutes. OK?"node scripts/texture.mjs --level my-level.glb --id my-level --views 90 --spacing 3.5 --aerial 8 --roofs 24 --holes 12 --atlas 8192 --plan-only - Try a few views.
scripts/prompt-test.mjsrepaints chosen views of a saved run for cents, and--dryshows the prompts without calling a model. Show the person. The words must name materials only, because anything else named gets painted onto walls: "weathered limestone and ochre plaster walls, terracotta roofs", never "a fountain". Don't ask for a named game's or studio's look. - Paint it. The same command without
--plan-only, adding--style "your materials". A run is saved only at the very end, toruns/<id>/, so a crash loses what it has paid for: keep big runs to small batches. - Fill the gaps.
--resume <id> --views 30plans views only where the texture is missing or poor, and paints on top. Narrow gaps between buildings often stay gray. - Make it game-sized. You get
runs/<id>/level_textured.glbandtexture.png, an 18 to 99 MB file. Shrunk to 4K it's a few MB, for examplemagick texture.png -resize 4096x4096 -quality 85 texture.webp. Put it back on your own model (same UVs) if you need separate parts for doors, collision or breaking, because trompe's own output is one merged piece. - Play it and credit it. Add a line to the game's
CREDITS.md: the level was painted with trompe, using the models named below. Tick Patch's AI-generated disclosure when publishing.
By hand
npm run dev # http://localhost:5178, reads FAL_KEY from the environment
Pick a sample or drop in your own .glb, describe the materials, press Plan cameras, then Texture level, and download the textured .glb or the texture.
How it works
| Step | Where | What |
|---|---|---|
| Plan | src/core/planner.ts | Finds walkable floors at eye height, keeps the spots a player can reach, and picks cameras greedily until most of the surface is seen well. Roofs and outer walls get cameras of their own, then a hole pass goes after what's left. |
| See | src/core/clay.ts, views.ts | Renders each camera's gray view under overcast light, with what's already painted composited in. Painting under cloud is why the texture comes out close to plain colour, with no shadows in it. |
| Prompt | src/core/semantics.ts, src/providers/models.ts | Builds each view's prompt from the part names it can see. |
| Paint | src/providers/ | FLUX 3 Klein paints a view that's mostly new (about 1¢), and Fibo 2 Edit fills the rest and every roof (4¢). One at a time, so each view sees everything painted before it. |
| Paste | src/core/composite.ts | Keeps known pixels exactly and shifts the new image's colours to meet them at the seam. |
| Project | src/core/atlas.ts | Writes colour into each texel this camera sees better than whatever's stored. |
| Bake | src/core/export.ts | Pads the texture's gutters and exports glTF. |
--lighting outdoor paints a sun's shadows in instead. It can look photographic from that one sun, but it can't be relit.
What it's bad at
- The ground up close. Each metre of surface gets about 50 to 85 pixels across. From a player's eye height, it's a blurry smear.
- Detail is flat. Windows and doors are paint, not shape, and there's only colour, with no surface detail or shine. Close up and at an angle, the trick shows.
- Gaps and mistakes. Bare patches, gray slots between buildings, paint in the wrong place under overhangs. These are visible in the demo: gray left on the level is what the run left.
- It's all one piece. Doors, collision and breakable parts are lost unless you put the texture back on your own model.
- Style. Every prompt says "photorealistic photo". The hand-painted runs we tried came out as a painted version of realism.
The demo
demo/ is the demo on this page. It's a replay, and demo/bake/bake.mjs makes its files from a saved run with no model calls:
- Real: the level, the order of the views, each view's price and time, the pictures in the corner, the cameras, the finished texture and the sun.
- Simplified: when a view comes, a texel shows its finished colour, not what that view first painted. Later views improve some patches, so part-way through the demo looks a little better than the run really did. This is why scrubbing never stalls: there's one texture and one number in a shader.
- Not shown: the painting itself. It takes about 10 seconds a view and costs money.
Quality tiers are chosen by graphics card (?tier=high, mid or low to force one). low uses a 2K texture and smaller shadows, and any tier lowers its resolution if frames run slow. To rebuild, after a run:
git lfs pull # the 8K texture is in Git LFS
node demo/bake/bake.mjs --level samples/village.glb --run village-demo # needs Python with Pillow
cd demo && npm run build
Licence
The code is Apache-2.0, see LICENSE. The Patch name and mark aren't covered by it.
The demo's painted art is CC0, with the models and the service credited below. LICENCE-NOTES.md records why.
What you paint isn't covered by that. It comes from your own key, so Patch's CC0 doesn't pass to your results, and neither Patch nor trompe can promise more than the image service and the models do. Read their terms for your use.
Credits
Made by Patch. Image models run on fal: FLUX 3 Klein by Black Forest Labs for first views, Fibo 2 Edit by Bria for fills and roofs. Built on three.js, three-mesh-bvh, Vite and Playwright. Also on GitHub.