React + TypeScript + Vite. Deployed from GitHub to Cloudflare Pages, which
builds and serves dist/ — only index.html and whatever public/ contains
reach the web.
npm install
npm run dev # localhost:5173
npm run check # validate the sheet — run after moving any cell
npm run lint
npm run build
npm run capture # re-photograph the four live project sites
npm run film-ground # re-bake the film texture imagesThe desktop layout is a contact sheet of film seen through a lens. MICROFICHE_PLAN.md has the design brief.
src/
projects.ts the five projects — the site's content
microfiche/
index.ts public surface: MicroficheViewer, clusters
sheetData.ts every cell and its coordinates
SheetCell.tsx how each kind and variant draws
MicroficheViewer.tsx the lens: travel, dragging, keyboard, sound
microfiche.css
public/microfiche/
captures/ generated by `npm run capture` — never edit by hand
archive/ hand-placed images and baked textures
Everything sits at a fixed coordinate on one sheet, 1 unit = 100px. The viewer moves a lens over it at a single fixed magnification; nothing zooms.
-
Put images in
public/microfiche/archive/. Notcaptures/—npm run capturedeletes that folder. Flatten transparency first, or the film's black shows through:sips -s format jpeg x.png --out public/microfiche/archive/x.jpg -
Add a cell to
sheetData.tswith anid, akind, andx, y, w, hin units.kind is needs capturean image src,labelplatedrawn in code variant, usuallytitleandlinesclippingthe description headline,bodycoverthe index card nothing — built from the project record -
Two optional flags.
projectputs the cell in that cluster, so it brightens with that project and clicking it centres there; without it the cell is transitional filler between clusters.framedadds it to the block the lens centres on — keep a cluster's framed block under about 8 × 5.5 units or it will not fit the lens. -
Set
fit: "contain"on diagrams. The default crops to fill, which is right for a screenshot and wrong for a figure. -
Run
npm run check. Cells are hand-placed and an overlap raises no error, it just looks wrong. It catches overlaps, cells off the sheet, duplicate ids and missing images. -
If the content outgrows the sheet, change
SHEETinsheetData.ts; the stylesheet follows it.
To add a new plate variant: add the name to PlateVariant, add a case to
Plate in SheetCell.tsx wrapping the contents in <Body>, and style
.fiche-<style> if it needs its own treatment. Several variants share one
style, so a plain ruled list can reuse style="record" and need no CSS.
npm run capture steps the parking chart back to the last month its collector
populated (it stopped in April 2026) and waits a long time for the satellite
globe to draw — don't shorten those.
The sheet is far too large to hold as one texture, so the browser rasterises it in tiles on demand. Anything painted across it costs frames while panning, which is why the mottling and grid are baked images rather than CSS gradients. Keep it that way.