Forensics — art production specification (Phase 1: The Alley)
Status: drafted 2026-10-13 by Claude for Sarah's review. Supersedes the pilot brief's art sections where the two differ; the brief remains the statement of goals.
This spec commissions every painted asset for the first Forensics environment. The alley's geometry, cameras and slots already exist and are checked. The painter's job is to make one coherent, attractive place that sits exactly on that geometry. Everything that is a number, a line or a label is drawn by the app and never painted.
Phase 2 (the parking lot) has its own spec, docs/spec/forensics-parking-art-spec.md (review page 2026-10-14-forensics-next.html). Its geometry was authored and checked on 2026-10-14, before its art was commissioned.
1. Sources of truth
| What | File | Rule |
|---|---|---|
| Geometry, cameras, lights, slots | forensics/alley.scene.json | Authoritative. The painting conforms to it. |
| Props, sizes, painted variants | forensics/props.json | Authoritative. |
| Painter's guides | forensics/guides/alley_<view>.guide.svg | Wireframe of the architecture and every slot, rendered from each plate's camera at 1536 × 2304. Use it as the structural reference for the painting. |
| Control points | forensics/guides/alley_<view>.points.json | Named world points and their pixel positions. Used for acceptance (section 8). |
| Cases the art must serve | forensics/fx-a1-*.json, fx-a2-*, fx-a3-* | Six cases, three investigations. |
| Narrative-world style | ART_DIRECTION | Applies in full, including its visual QA checklist. |
| Asset handling | ASSET_PRODUCTION | Applies in full: masters, honest resolution, transparency, manifest. |
| Contract and checker | forensics-scene-contract.md, forensics/check_forensics.py | Regenerate the guides with python3 docs/spec/forensics/check_forensics.py --guides docs/spec/forensics/guides. |
If the geometry makes a composition impossible, report it. Do not paint around it. The geometry changes only through the scene file, after which the guides are regenerated and every case is rechecked.
2. The place
An older service alley behind a commercial block, at night, deserted but in use. Twenty metres long and five and a half wide, dead-ended by a concrete-block wall at one end, opening onto a cross street at the other.
| Element | Description | Geometry |
|---|---|---|
| Wall A (right side of the wide view) | Aged red-brown brick, four storeys, mortar dark with soot. The building's working side. | wall_a, 9.0 m |
| Second-floor window | Double-hung wooden sash, lower sash raised (open). Painted frame, chipped. Warm lamplight inside, seen as a lit interior, not a glow. | window_2f: sill 3.6 m, head 5.0 m, 1.0 m wide |
| Service door | Steel, painted blue-gray, scuffed at kick height. No exterior handle; a plain keyhole plate. Steel frame, concrete step flush with the pavement. | service_door: 0.9 × 2.1 m |
| Door lamp | Small caged wall fixture above the door, warm incandescent. | door_lamp fixture |
| Fire-escape landing | Black steel landing and railing, drop ladder stowed up. Toward the street end, nowhere near the window. | fire_escape, 3.2–3.35 m |
| Wall B (left, mostly out of frame) | Brick painted cream long ago, gone gray. | wall_b, 7.5 m |
| Dead-end wall | Concrete block, stained. | wall_end |
| Pavement | Old asphalt, patched with concrete toward the street end. Dry. Grime and grit at the wall bases, a shallow drainage channel down the centre is allowed if it stays flat and unobstructed. | alley_floor |
| Street | Cross street beyond the mouth: kerb, a lamppost's light falling on the far side, dark storefront. Seen in the wide view and the plan. | street |
Lighting. Three sources, as in the scene file: the window's warm interior light spilling onto Wall A above the sill, the door lamp's pool on the pavement around the door, and a cool ambient night fill. The street end is slightly brighter from the street lamp. Pavement is dry: it takes the lamp pool as a matte warm patch with no reflections or sheen. Shadows fall consistently away from each source.
Permitted set dressing. Flat wall detail that creates no measurable surface: downpipes, conduit, a dead meter box, old fixings, a ghost of a painted sign with no legible lettering. Anything that occupies floor space or could be mistaken for a measurable object must be in the geometry first: no extra bins, pallets, vehicles, ladders, crates or doors in the plates.
Never in a plate. People, text or numbers, measurement graphics, evidence markers, any prop listed in section 4, wet pavement, glowing windows, bloom, neon, hanging lamps. The plate must look complete and attractive with every prop and overlay removed.
3. Plates
All plates are opaque, text-free and prop-free. Portrait 2:3. Paint at 1536 × 2304 if the tool supports it; the minimum acceptable native width is 1170 px (one iPhone screen width at 3×). Record the true native resolution in the manifest. An enlarged image is not a master.
| Plate | View | Camera | Must show clearly |
|---|---|---|---|
alley_scene_wide | Establishing, eye height, from near the dead end toward the street. Wall A runs down the right. | scene_wide | Window (large, upper right), door, fire-escape landing, the alley mouth and street in the distance, open pavement where the ladder lies |
alley_scene_door | Closer view from the street end looking back at the door. | scene_door | Door (about a third of the frame width), the stretch of Wall A where the flat crate leans, pavement around the door |
alley_plan | Overhead, orthographic, street at the top. | plan | Walls as thick top edges, the door and window positions marked by their sills/thresholds, pavement texture, the street. Reads as an illustrated site plan at true scale: 1 m = 96 px at 1536 wide. |
The two perspective plates show the same place: brick colour, mortar, door paint, window frame, lamp and wear must match between them. The plan must agree with both.
Keep the top 8% and bottom 20% of each plate visually quiet. The app's header and tools sit there, and the plate may pan within the viewport.
The three guides
4. Props and decals
Every prop is painted in place: on a copy of its plate, in its slot, matching the guide's outline, then delivered as a full-canvas transparent PNG registered pixel-for-pixel to the plate. Its cast shadow is a separate full-canvas layer (multiply). Contact darkening where the object meets the ground belongs to the prop layer. A prop is painted once per view and size it appears in.
| Prop | Sizes | Views | Layers |
|---|---|---|---|
| Extension ladder, aluminium, lying on the pavement | 5.4 m, 4.8 m | scene_wide, plan | 4 prop + 4 shadow |
| Dumpster, steel, lid closed | one | scene_wide, plan | 2 + 2 |
| Equipment cases, stacked two high | one | scene_wide, plan | 2 + 2 |
| Flat packing crate, opened and empty, standing on its long edge against Wall A, straw spilling | 2.5 × 2.2 m, 2.5 × 2.4 m | scene_door, plan | 4 + 4 |
| Ladder scuffs (ground decal): two short scrapes side by side along the wall, one under each ladder rail: 0.39 m between inside edges, 0.45 m between outside edges | one | scene_wide, plan | 2, no shadow |
| Footprints (ground decal): a heel smear and a partial sole print, turning | one | scene_wide, plan | 2, no shadow |
Total: 16 prop layers and 12 shadow layers. The lengths are exact. A 4.8 m ladder painted at the 5.4 m length fails acceptance, because students measure it.
The evidence marker, every measurement line, the ghost ladder, routes and outlines are drawn by the app.
Occlusion. No foreground element passes in front of a slot in these three plates, so no occlusion masks are needed. If a revision adds one, it needs a mask layer per plate.
Layer order. Plate, prop shadows, props, foreground occluders, then the app's overlays and UI.
5. Painting to the guide
The guide is the structure; the painting is the finish. Plates use forensics/guides/alley_<view>.guide-lines.png; props use the per-layer guides in forensics/guides/props/. Guides are regenerated whenever a plate's camera is refitted, so use the current ones (the plan guide and the per-layer guides were first written 2026-10-13, after all three plates were accepted). Use the guide as the composition and perspective reference (as a conditioning or reference image if the tool is an image generator). Architecture edges, opening corners, slot outlines and the floor plane must land on the guide. Style references: the approved Peregrine environment art in Image-Assets/ (The Raid's locations and the Darkroom).
For props, the image model paints over a geometry-locked render, never freehand: check_forensics.py --blockouts <dir> renders each prop flat-shaded in place from the calibrated camera at native resolution, with an edit mask, native-resolution 1024 × 1536 crops and their offsets. The edit restyles the surface; the layer is then cut with the render's own silhouette, so the outline registers by construction. (Added 2026-10-13, after a freehand ladder missed its slot by about 80 px.)
Lines that converge in the guide must converge in the painting. A tool that cannot hold a wireframe will fail acceptance, and the fix is a better-registered painting.
6. Deliverables
Into Image-Assets/Forensics/Alley/:
plates/: the three opaque masters.props/<view>/: the 16 prop layers and 12 shadow layers, named<prop>[_<size>]_<view>.pngand<prop>[_<size>]_<view>.shadow.png, with the prop ids and sizes offorensics/props.json(for exampleladder_ext_4.8_scene_wide.png,crate_flat_2.5x2.2_scene_door.png,scuffs_plan.png).forensics/guides/alley.prop-layers.jsonlists all 16 with their guides.source/: layered source files if the tool produces them. If it does not, say so in the manifest.manifest.json: every file with dimensions, native resolution, alpha, byte size, SHA-256, view, prop and size. Same shape as The Raid's.specs/prompts.jsonif generated: exact prompts and the guide and references used for each image.review/: a contact sheet per view (plate alone; plate with each prop; plate with guide overlay) and a phone-size preview of each plate at 390 pt wide.
7. Process
- Plate proof. Paint
alley_scene_wideonly. Run the calibration check (section 8). Sarah reviews composition, mood and fit before anything else is painted. - Matching plates. Paint
alley_scene_doorandalley_planagainst the approved wide plate's materials and light. - Props. Paint the ladder at both lengths first, in both views, and run the length check. Then the rest.
- Assembly review. Composite every case's staging over its plates with the app's overlays (
check_forensics.pyoutput beside the composite) and review at phone size.
8. Acceptance
Calibration. Mark the control points visible in the painting (world point and measured pixel position) in forensics/calibration/<plate>.observed.json, then run check_forensics.py --fit <that file>. The tool re-solves the plate's camera from the marks and reports each point's miss. A point within 0.75 m of anything a case measures on that plate is case-critical and must land within 1% of the plate width (15 px at 1536); every other point must land within 2% (31 px). The tool refuses fewer than 6 marks, and refuses marks that all lie on one plane unless every case anchor on that plate lies within 0.3 m of it; on the wide plate, floor marks (the alley-mouth kerb) supply the off-wall constraint. Overhead plates fit centre and scale with rotation held. When the fit passes and every scene and case check still passes with it, --apply writes the camera into the scene file, and the guides are regenerated so props register to the painting. A plate whose fit fails is repainted or retouched. (Rule amended 2026-10-13, Sarah.)
Prop registration. Each prop layer is painted to its own guide, forensics/guides/props/<prop>[_<size>]_<view>.guide.png: the architecture in gray, that one prop at that one size in blue, its anchors circled in red. Run check_forensics.py --check-props Image-Assets/Forensics/Alley --props-review <dir>. It measures each layer against the projected outline: the painted extent along the prop's long axis and the bounding box, within 1% of plate width for the ladder and the flat crate (case-critical: A1 and A3 measure them) and 2% for the rest; a decal's centroid within 1% of its point. Every layer must be 1536 × 2304 RGBA, and every prop with a shadow must have its shadow layer. The review overlays show the plate, shadow and prop with the expected outline.
Reuse. Removing every prop leaves a complete, attractive plate. Each prop can be toggled without visible seams, halos or missing shadow.
Consistency. Materials, wear and light agree across the three plates. The window, door and fire escape match in every view they appear in.
Style. ART_DIRECTION's visual QA checklist, every item recorded. Dry pavement. No unrequested additions.
Phone. At 390 pt width the window, door, ladder, crates and decals are identifiable and tappable, and the quiet bands hold the header and tools without fighting the art.
Cases. All six alley cases pass check_forensics.py, and their composites read correctly: in A1 the ladder and the scuffs below the window, in A2 the footprints past the stacked cases with the route to the street, in A3 the door and the flat crate side by side.
9. Open items
- Plate 1 accepted 2026-10-13 (
alley_scene_wide, gpt-image-2, native 1536 × 2304). Refit 2026-10-13 with floor marks on the alley-mouth kerb added, because Wall A marks alone are coplanar: 9.5 px RMS, 19.3 px worst (far corner foot, not case-critical); case-critical points within 8.4 px. The guide the API received is kept, hash-verified, atforensics/calibration/alley_scene_wide.guide-lines.authored-camera.png. - Door plate accepted 2026-10-13 (
alley_scene_door). Door corners, the lamp and four wall-base marks fit one camera. All marks lie on Wall A; the fit is allowed because A3's door and crate face sit within 0.3 m of that plane. The door lamp was lowered in the geometry to 2.32–2.52 m, the height both plates painted it. Work's own 4-point fit was underdetermined; the tool now refuses fewer than 6 marks. A masked retouch the same day replaced the wire-cage lamp with plate 1's paned lantern (edits confined to x 395–554, y 490–639; zero pixels changed outside the mask). Re-measured after the retouch: 7.4 px RMS, 12.0 px worst (lamp); the case-critical door foot is within 11.6 px. A second masked pass shifted the leaf toward plate 1's blue-gray (chroma only, inside the frame, keyhole untouched); the leaf's mean colour now sits within 3 levels of plate 1's in every channel. Refit after it: unchanged, 7.4 px RMS. - Plan accepted 2026-10-13 (
alley_plan), after a masked retouch of the Wall A window strip (x 1024–1074, y 1490–1759; zero pixels changed outside it). Window re-marked at y 1514–1610. All eight marks within 7.2 px, 4.4 px RMS; centre (11.265, 2.852), 15.976 m across. A second masked pass the same day fixed the two flaws left by the first: the brick strip again has the original course pattern and dark inner-face band, and the window is drawn from straight above as a sill and frame top, with no glass panes. The window still spans y 1514–1610, and the refit is unchanged. - Props accepted 2026-10-13. All 16 prop layers and 12 shadow layers pass
--check-propsand visual review, after a section-by-section redo of the wide ladders and one fix round (scuffs, wide footprints, wide dumpster, a glare patch on the 5.4 m ladder). The scuffs and footprints are faint at 390 pt; the app's evidence marker carries them, and their contrast can be raised in code if play shows a need. - Assembly review 2026-10-13 (section 7, step 4):
check_forensics.py --assemblecomposites every case over its plates with the app overlay, at phone width (assembly review). All six cases read as section 8 requires. In the plan overlay, measurement labels collide with mark labels (A1swith E1, A2bwith F1); that is app overlay layout, settled at integration. - Alley complete 2026-10-14. The scuffs were repainted side by side along the wall (slot rotation corrected, 0.39 m between inside edges and 0.45 m between outside edges), and the guides were regenerated. The dumpster and the decals read at phone size through a display tone in code, set out in the pilot notes.
- Wall A in plate 1 is uniform brick edge to edge (ART_DIRECTION's "indiscriminate texture"). A masked touch-up (a downpipe, calmer upper brick) stays optional; any touch-up is re-measured with
--fit. - The plate's display size in the app is not fixed. The 2:3 plate, quiet bands and panning cover the likely layouts; the first app integration confirms them.