Archive Light Table 档案灯台

Two Seconds Short·40s·Lemo-Opuscarby Lemomo

Real archive objects (glass negatives, slides, letters, telegrams, clippings, drawings) lie on a cool, backlit light table. A loupe comes down on one of them, and inside the glass the object comes alive: a negative turns positive, a line of type lights up, a number lifts off the page, a photograph opens into depth. Precise catalogue numbers, dates and readings are pinned to the objects. Clean, modern and quiet, with no vintage filter.

The demo: The telegram said the longest flight lasted 57 seconds and the newspaper printed it; under a loupe, the Wright brothers' glass negatives and the Library of Congress record say 59.

Brand history & anniversariesDocumentary & archive explainersMuseum & collection stories
Style prompt · STYLE.md

Real archive objects (glass negatives, slides, letters, telegrams, clippings, drawings) lie on a cool, backlit light table. A loupe comes down on one of them, and inside the glass the object comes alive: a negative turns positive, a line of type lights up, a number lifts off the page, a photograph opens into depth. Precise catalogue numbers, dates and readings are pinned to the objects. Clean, modern and quiet, with no vintage filter. References (grammar only): archive documentaries that push into still photographs; visual investigations that annotate real imagery (one line, one reading, one source per screen); product macro films (glass edges, transmitted light). Copy none of their layouts, typefaces, shots or music, and never name them in the film.

1. Essence, and what it is not

  • The objects are real: scans of public-domain or licensed archive items, at their true proportions, with their real damage (broken corners, emulsion edges, folds, pencil marks). Nothing in them is drawn or generated.
  • Light comes from below: the table glows a near-white cool grey; film and glass transmit it, paper glows warm at its edges, opaque things read darker than the table.
  • The loupe is the camera's door: every dive into an object goes through a loupe that lands on it. The inside of the lens is where the object changes state.
  • Catalogue truth: every object carries its real identifier, date and title from its holder; numbers on screen come from the record.
  • One signal colour for marks, readings and anchors; everything else is the archive's own greys and paper tones.

Sources and licences. Open collections with masters and catalogue records: the Library of Congress (files on tile.loc.gov; records from its SRU server lx2.loc.gov:210/lcdb when loc.gov pages are blocked), Smithsonian Open Access (CC0), the Rijksmuseum (CC0), NARA, Europeana (check each item's rights statement). CC0, public domain and CC BY (credited) are fine. "No known restrictions", CC BY-SA and other terms are not CC0: name the terms to the user and ask. For brand history, ask the user for their scans, archive numbers and right to use them. Never stand a generated image in for an archive object. Keep a FACTS.md in the project folder (claim, source URL, quoted wording, or how a figure was derived) and deliver it with the film.

Not Halftone Dossier (no newsprint dots, no aged-paper grade), not Satellite Briefing (objects on a table, not the ground from orbit), not Data Storytelling (no charts; the numbers live on the objects), not a keynote (no slides or header bars).

2. Materials & rendering

  • One world, one camera: every object is a textured quad on the table with a centre, rotation and scale (texture px → world units at true size). The camera is a 2D centre, zoom and roll; the table and every mark go through the same world → screen function.
  • The light table: an emissive surface (≈ 0.97 white, slightly cool), soft falloff, diffuser noise, an aluminium frame and a dark desk beyond. It can be switched off (ambient ≈ 0.04) and flicker on.
  • Glass negatives: transmission from the scan's positive, T = 0.035 + 0.9 · (1 − pos)^1.25, with a warm silver-gelatin tint; the emulsion mask comes from the scan (white margin touching the border = no glass), so broken corners show the table. Dust and scratches are procedural, anchored to the table.
  • Paper: backlit, its colour is transmission (paper tone × light); lit lines get darker ink and brighter paper; unlit text can dim to bring one line forward.
  • The loupe: inside the lens each pixel maps back to the table through the magnification (≈ 2.5×) with a pincushion term (k ≈ 0.16 at the rim), slightly different per RGB channel for colour fringes. Around it: a black ring with a bevel highlight and a clear acrylic skirt that refracts the table and glows at its foot. Landing and lifting, it scales by about 1.35× and casts a soft shadow.
  • The photograph space: after the push the lens stops being a lens. A photo gains depth from a ground-plane nearness map (zero at the horizon, linear in the row below it), cut-out layers for upright subjects and a filled background; a dolly and a lateral shift drive parallax, and a subject layer may drift. Things left on the ground plane lean: centre the dolly between them.
  • Sampling: all texture reads inside the branching lens code use explicit gradients (textureGrad), otherwise quads that cross a branch pick a wrong mip level and leave dashed seams.
  • Marks live on a 2D canvas above the picture: hairlines 1.5–3 px, rings, crop corners, underlines, leaders. Table marks are clipped out under the loupe; marks inside a photograph use its forward mapping, parallax included.
  • Post: a light vignette and fine moving grain (heavier inside photographs). No film damage, no sepia, no halftone.

3. Colour logic

  • Neutral ground: the light table (near-white, cool) and the archive's own tones (silver greys, paper tans). Do not regrade the archive into a colour scheme.
  • One signal colour (e.g. a signal orange) for marks, readings, anchors and the verdict; never as a fill behind pictures, never for decoration.
  • Text is ink (near-black) on light areas and paper-white on dark caption plates; choose the contrast by the ground, never colour on colour.
  • Positives inside the lens may be gently levelled (black and white points) so a weak scan reads; negatives stay as the glass gives them.

Example (our demo): table #F7F9FB, ink #15171A, signal #FF5A1F, caption plates rgba(14,15,17,0.74).

4. Type & subtitles

  • Roles: a monospace for identifiers, dates and readings (e.g. IBM Plex Mono, 17–34 px; readings 74–120 px); a sans for labels in the film's language (e.g. Noto Sans SC or Inter, 21–36 px); a serif only for titles and the one name a photograph carries (60–76 px). OFL, self-hosted, CJK subset when needed.
  • Labels attach to objects: an anchor dot and ring, a vertical leader, then the text. A reading sits in a dark caption plate with a small label above and the source (the catalogue number or the original wording) below.
  • Subtitles are a caption strip at the bottom centre, on a dark plate with a 3 px signal-coloured edge. If the film is bilingual, the film's language goes on top. Hold ≥ max(1.8 s, speech + 0.6 s).
  • Quote the source when you can: a crop of the actual line, or the record's wording in quotation marks, beats a paraphrase.

5. Motion quality

  • 24 fps on ones; every camera parameter on smooth monotone curves through keys (no overshoot, no stepping).
  • The camera glides across the table, slows to let the loupe land, then pushes. Speed lives in the glides and pushes; holds are for reading.
  • The loupe lands with a fast ease-out (≈ 0.4 s), lifts the same way. Lines draw on in 0.3–0.6 s; text wipes in from its leader side; numbers count up and land with the voice.
  • What never moves: the archive's own content outside a photograph space, and the table itself.

6. Camera grammar

Move What it expresses Can serve
table glide (pan and roll) surveying; moving to the next piece of evidence a timeline of documents; a product family; an album
loupe landing + push into the lens choosing one object; "look closer" the claim; the key photograph; a signature
inside the lens: tracking along a line reading a letter, a ledger line, a caption, a spec
photograph space: dolly + parallax being there the event itself; the founding moment; the place
rise out of the lens stepping back, the evidence noted a transition; a summary
pull back to the whole table scale; everything at once an index, a timeline, a closing

Framing: at a reading moment the subject fills at least a third of the frame height; readings and labels stay clear of the subtitle strip (bottom 140 px). Transitions are made by the loupe (land, push, rise, lift) and the glide; no cuts, fades to black or page turns between objects.

7. Sound palette

  • Ambience: the light table's ballast hum (100 Hz and harmonics), starter clicks, room tone. Inside a photograph the hum gives way to that place's air.
  • Foley: the loupe on glass (a clack with a high ring), paper sweeps for glides and lit lines, ticks for scales and counters, a pen stroke for a verdict, soft clicks as labels land.
  • Music: restrained and small: a felt piano, a few strings, glass or bowed metal for the moments of seeing. Build by subdivision and register, not by drums. Leave real silences before a reveal or a verdict; the first sound after them should be the loupe, the place or the verdict.
  • Voice: a calm narrator, short sentences that state what the object says. Music about 10 dB under the voice, ducked while it speaks; −14 LUFS.

8. Native moves

  • Negative to positive under the loupe. A develop front spreads from the lens centre with a thin warm edge, turning the glass negative into its positive only inside the lens. Fits content like: a founder's portrait plate; a factory on a lantern slide; an X-ray film becoming the product inside.
  • The line lights up. On paper inside the lens one line of type brightens while the rest dims, an underline draws under it, and one word or number lifts out into a reading card. Fits content like: a ledger entry; a patent claim; the first order on an invoice.
  • Into the photograph. The lens rim grows past the frame while the view inside widens; the photograph gains layered depth and a subject drifts. Fits content like: the first shop front; a launch day crowd; an expedition camp.
  • Measuring on the photograph. A ruler laid on the ground plane in perspective, with ticks that crowd toward the horizon and a counter that lands on the recorded figure, marked as a schematic when it is one. Fits content like: a race distance; a bridge span; a building's height.
  • Two records, held side by side. Two caption cards, each with a crop of its source, held still until both are read before one is struck. Fits content like: a misprinted date; two versions of a founding story; specification against delivery.
  • The table becomes the index. Pull back to the whole table; each object's tag flies into one list with its identifier and its reading. Fits content like: a company timeline; a collection catalogue; a product family.

9. Pitfalls of the medium

  • Over-magnifying a weak scan: keep the lens interior under about 1.5 screen px per scan px; choose items by scan size.
  • The loupe sweeping over empty table: lay objects close together, land the loupe straight onto the object (drop in, don't slide in), and glide the camera over objects, not gaps.
  • Shear in the photograph space: one depth map for a photo with people or machines in it tears them apart. Cut upright subjects into layers, fill behind them, and keep 2.5D moves small enough that the fill is barely seen.
  • Labels that scroll away: a label on a moving table leaves the frame before it is read; hold the camera, or fix the caption on screen.
  • Vintage creep: no sepia, no halftone, no paper-ageing overlay. The age is in the objects, not the grade.

10. Engine

  • demo/film.js: IT (objects: centre, rotation, scale), toW() / toT() (texture ↔ world), CAM keys and w2s(), dive() (loupe point, land/lift times, lens interior as tracks), shot(t), int2s() (photograph → screen for marks), the shader (lightTable, paper, negative, parallax, rim and skirt), the overlay (annot, measure, compare, indexPanel).
  • demo/tools/prep.py: emulsion masks from scans, difference matting of a subject against a smooth sky, ground-only cuts, push-pull fill.
  • Minimal example, not in the demo: put one 35 mm slide (IT.slide = {c, r, k, size}), a loupe landing with dive(toW(IT.slide, p), t0, t1, keysS, keysC), and light one line with band().
  • Extra packages and packs: none beyond setup.sh deps voice music (numpy, scipy, Pillow, soxr are in the core tier). A score in this palette needs sample libraries: sh tools/fetch.sh instruments salamander (piano), vsco2ce (strings), vcsl (glass, bowed vibraphone). Anything else a film needs goes into a private venv or node_modules in the project folder, never into $LIB/.venv.

11. Variation space

The agent decides the objects, their order, the story, which native moves to spend and where, the camera path, the subject of the photograph space, and the signal colour.

  • Three structures: a company's founding told as five objects in date order, each dive revealing one person; a single photograph explored in three dives (the place, the people, the date on the back); a before/after pair of plans and photographs of one building.
  • Three openings: the table switching on in the dark; a loupe already on a plate, rising to reveal the table; a hand-written date on an envelope filling the frame.
  • Three endings: the index of everything on the table; the last photograph held in depth with one line of text; the light table switching off, leaving one object glowing.

How our demo was made (story, shots, score, end card, build): DEMO.md. Read it after your treatment exists.

Director's notes for the demo film

One example among many. Don't reuse its story, arc, shots, props or timings.

Demo: Two Seconds Short 《差两秒》 (40.0 s) · archive-lighttable.mp4 · source in demo/

The Wright brothers' flights of 17 December 1903 on a light table, told through five Library of Congress items: two glass negatives, a telegram, a newspaper clipping, and a third negative as a prop. It is an unofficial film about a real event, made only from public-domain scans. Chinese on screen, English narration, bilingual subtitles (Chinese line above, English below). Every fact and its source is in demo/FACTS.md.

Story & structure

Logline: the telegram said the longest flight lasted 57 seconds, and the next day's paper printed it; the glass negatives and the Library's record say 59.

Arc: the claim (the table switches on; a loupe lands on Orville's telegram; inside the lens the lines light up and "57" lifts out as a reading) → the echo (the camera rises and glides to the Dayton Herald clipping, where the same sentence is underlined and its 57 ringed) → the evidence, which is also the emotional peak (the loupe lands on the first-flight negative, the negative turns positive inside the lens, the camera falls through the lens into the photograph, and the Flyer separates from the horizon and drifts forward; 12 s, 120 ft) → the measurement (the fourth-flight negative: a ruler laid on the ground plane from the camera to the tiny machine on the horizon; 852 ft, 59 s) → the verdict (57 and 59 side by side, held still 2.7 s, then 57 is struck: "差 2 秒") → the ending (pull back to the whole table; every object's tag flies into one archive index; end card).

Native moves spent: the line lights up on the telegram and the clipping; negative to positive under the loupe and into the photograph for the first flight (the signature, at the peak); measuring on the photograph for the fourth flight; two records, held side by side for the verdict; the table becomes the index for the ending.

Shots

One continuous camera over one table; framing changes by zoom and by passing through the loupe. Times in seconds.

# Time Shot Camera Native move / note
1 0.0–1.3 the dark table flickers on (0.0, 0.17, 0.42, 0.55; steady at 0.67); the telegram fills two thirds of the frame, the clipping at the bottom edge glide toward the telegram with a 4° roll that levels it the starter clicks and hum are the first sounds
2 1.3–2.0 the loupe drops onto the telegram (scale 1.35 → 1, shadow tightening) settles glass clack on the beat
3 2.0–3.3 push into the lens: the rim passes the frame while the view inside widens to the whole message zoom 0.31 → 1.5 the date line lights at 2.67
4 3.3–9.3 inside the telegram: "Success four flights …" lights at 4.0, "… longest 57 seconds …" at 7.33, each with an underline; the rest of the paper dims; 57 is ringed at 8.0 and a reading card "电报 · 最长一次 57 秒" draws out to the right slow track along the lines, then a push onto 57 reading
5 9.3–11.3 rise out of the lens; the loupe lifts at 10.33; the card stays on screen until 11.4 zoom out and glide down-right a ring stays on the telegram's 57
6 11.3–13.3 the Dayton Herald clipping: "longest time in air was 57 seconds." lights and underlines at 12.0, its 57 ringed at 12.33; a caption plate "1903.12.18 / 代顿先驱报" held still on the right push in with a 4° roll the echo
7 13.3–14.7 glide to the first-flight negative; crop corners (the 16:9 frame the photograph will open to) glide and settle the loupe lands at 14.67
8 14.7–16.0 inside the lens the negative; a develop front from the centre turns it positive (15.0–15.8); readout "×2.5 负像 → 正像" hold
9 16.0–18.0 signature: the rim grows past the frame while the view inside widens, under a riser and the strings; at 17.6 the sound is sucked out; at 18.0 we are inside the photograph zoom 0.335 → 1.45; inside 0.84 → 0.56 px per scan px no narration through the push; the full chord lands at 18.0 and the narrator starts at 18.35
10 18.0–22.7 the photograph in depth: the Flyer (its own layer) drifts 125 px forward and up against the horizon; the sand swells under a dolly; wind over the dunes; labels "第一次飞行 / 12 秒 · 120 英尺 · 上午 10:35", "威尔伯·莱特", "快门:John T. Daniels" dolly (0.55) + lateral shift (260, −140) about the Flyer's depth near-silence from 21.3: wind only
11 22.7–24.7 rise out of the photograph; glide over the 14 December plate to the fourth-flight negative zoom out, glide right
12 24.7–26.3 the loupe lands; push in; the positive already shows (the viewer stays switched on) zoom 0.31 → 1.45
13 26.3–30.0 the ruler: from the bottom edge (the camera, the start) to the machine on the horizon, ticks every 100 ft crowding into perspective, labels 100 / 200 / 400; the reading counts 852 ft (lands 28.67) and 59 s (lands 30.0); "落地点" ringed push toward the machine (0.62 → 1.3) with a ground-plane dolly ticks on every 100 ft
14 30.0–33.3 two caption cards: the telegram's own line (a crop of the scan) "57 秒", the record's wording "852 feet covered in 59 seconds." "59 秒"; held still 2.67 s; silence from 32.8 locked comparison held before the verdict
15 33.3–35.9 57 struck in signal orange, "差 2 秒" locked, then the rise begins under the cards at 35.0 the strike lands on low D after the silence
16 35.9–40.0 pull back to the whole light table in its frame on the dark desk; five tags fly into "档案索引" (the two 57s struck); end card at 36.33 zoom to 0.094, roll to level scale reveal

Score structure

90 BPM, 4/4; every key action on a beat or half-beat (demo/tools/cuecheck.mjs), except the two breaths (17.6, 32.8), which sit just before their downbeats by design. B minor turning to D major.

Section Time Palette Notes
Light 0–2.7 piano D5 with the light; hum and starter clicks
Telegram 2.7–10.7 felt piano broken chords in eighths, Bm–G–D–A; a paper sweep and a glass ring on each lit line; cellos D3 and violas A3 enter under the 57
Clipping 10.7–13.3 the same broken chords, quieter (Bm, G) an echo
First flight 13.3–23.6 D at the glide; strings swell from the develop (attack 1.6 s), piano climbs D4 → A5 in eighths and a noise riser rises through the push; everything is cut at 17.6; at 18.0 the full D chord (piano D2–A4, strings, contrabass D2, glass D6, a soft cymbal), then a falling line A4–F♯4–D4–E4–F♯4 wind and surf replace the hum inside the photograph
Measure 26.3–32.8 piano eighths on an A-major pedal, getting slightly louder; cellos A3 and violas E4 underneath; ticks every 100 ft; glass A5 at 852, E6 at 59 acceleration by density, not tempo
Verdict 32.8–33.3 silence (hum only)
33.3 pen stroke, piano D2/D3/A3, glass D6, soft timpani D3
End 35.7–40.0 the theme in D (F♯4–A4–D5–C♯5–A4), ticks as index rows land; the final D chord with strings and bowed vibraphone at 36.33

Mix (in mix.py): music about 11 dB under the voice before ducking, ducked a further ~5 dB while the narrator speaks; foley 9 dB under the voice, beds 24 dB under; a 38 Hz high-pass on everything but the voice; a master limiter keeps the peak-to-RMS ratio near 11.5 dB so mux.sh can reach −14 LUFS under −1.2 dBTP.

Palette & props

  • Table #F7F9FB with a brushed-aluminium frame and a dark desk (#0E0F11); ink #15171A; signal orange #FF5A1F; caption plates rgba(14,15,17,0.74).
  • Props (all Library of Congress, true scale, 5000 world units = 7 in): telegram (LC-USZ62-65459 / mcc.061), Dayton Herald clipping (LC-USZ62-70686), first-flight negative (LC-DIG-ppprs-00626), fourth-flight negative (LC-DIG-ppprs-00629), 14 December negative (LC-DIG-ppprs-00610).
  • The loupe: 2.5×, lens radius 1000 world units (≈ 1.4 in).
  • Narrator: Kokoro bm_george, speeds 0.92–1.0.

End card

Over the pulled-back table, bottom left: 差两秒 (Noto Serif SC), Two Seconds Short, a signal-orange rule, ARCHIVE LIGHT TABLE 档案灯台, LemoLab × Claude Opus 5.5; the archive index on the right. 3.7 s. The "LemoLab × Claude Opus 5.5" sign-off belongs to this library's demo only; a user's film carries no LemoLab credit and no copy of this card.

Build notes

demo/
  build.sh          one command: (FETCH) scans + textures + fonts → voice → speech check → cues → cue grid →
                    video → mix → master → .srt → subtitle widths → readcheck → poster + style frame
  index.html        two canvases (WebGL2 picture, 2D marks), fonts
  film.js           the table, camera keys, the three dives, the shader, every mark, readouts, index, end card, subtitles
  timeline.js       beats, key actions, narration, subtitle cues (single source; tools/export.mjs → out/timeline.json, out/cues.json)
  mix.py            score, foley, beds, voice, mix → out/mix.wav + out/stems/
  tools/            prep.py (masks, layers, fill), fonts.py, sru.py (LoC catalogue lookups), export.mjs,
                    cuecheck.mjs, subcheck.mjs
  sources/          the Library of Congress catalogue records (MARCXML) behind FACTS.md
  assets/           raw/ (downloads), tex/ (textures the page reads)
  FACTS.md, CREDITS, TREATMENT.md, PRODUCTION_LOG.md, stills/styleframe.jpg
  • sh demo/build.sh takes about 4 minutes on Apple silicon (the picture renders in about 40 s with 4 workers; the master pass is the slowest step). From a clean clone it first fetches what git does not hold: the fonts from Google Fonts (tools/fonts.py), the Kokoro model and the salamander, vsco2ce and vcsl sample libraries (tools/fetch.sh), and about 100 MB of Library of Congress scans, which tools/prep.py turns into textures (about 25 s). FETCH=1 forces the fonts and scans again.
  • Checks it runs: asr_check.py with small.en (two lines carry an asr field: Whisper writes "17th, 1903" for the spoken date and hears "four flights" as "for flights"), the cue grid, subtitle single-line widths measured with the real fonts (widest 648 px against 1500 px), readcheck.mjs.
  • Demo-specific pitfalls: loc.gov item pages sit behind a Cloudflare challenge, so the catalogue records come from the Library's SRU server (lx2.loc.gov:210/lcdb) and the files from tile.loc.gov; in the first-flight plate the sky can be unmixed exactly against a smooth membrane, but the sand cannot, so below the horizon only strong marks are cut (lower wing, skids, struts; a vertical-line filter catches thin struts) and the rest stays on the ground; the fourth-flight scan is low in contrast and gets its own levels in the positive; the clipping is a low-resolution copy, so the camera never pushes past about 0.66 zoom on it.