# PORTICO — asset provenance

Everything below is self-hosted in this folder; nothing is hotlinked and nothing is AI-generated. Downloads
were made on 9 Oct 2026. The architecture, the unseen portico, the chairs, music stands, instrument stands and
instruments (violin, viola, cello, double bass, pedal harp), the sky and the light are all built in code
(`js/portico.js`, `js/ensemble.js`) from published dimensions, not from models or images. Bows are not
modelled: at this sun a bow's shadow cannot read (see `js/ensemble.js`), so it is left out rather than faked.

Real photographs were used only to calibrate the shadows (v2, 10 Oct 2026). They are third-party work, kept in
`../tools/refs/` with their licences, and are never published: the whole `tools/` folder stays out of any
upload, artifact or deploy.

## Code

| File | Source | Licence | Notes |
|---|---|---|---|
| `vendor/three-0.185.1/three.module.min.js`, `three.core.min.js`, `LICENSE.txt` | npm `three@0.185.1`, `build/` (the same pinned copy the other concepts vendor) | MIT | Unmodified (the licence file renamed `.txt` so the host serves it). three.js's own shadow maps are switched off; the scene draws its own (see below). |

## Texture

| File | Source | Author | Licence | What was done |
|---|---|---|---|---|
| `tex/travertine-009-1k.webp` | ambientCG **Travertine009**, 2K-JPG colour map (`Travertine009_2K-JPG_Color.jpg` from `Travertine009_2K-JPG.zip`), https://ambientcg.com/view?id=Travertine009 (released 20 Dec 2023, 120 × 120 cm) | ambientCG (Lennart Demes) | CC0 1.0 | Converted to greyscale, resized 2048 → 1024 (Lanczos), normalised to a mean of 0.5 and a standard deviation of 0.16, saved as WebP q80. The shaders use it only as a **tone** (never as colour): each ashlar block and each paving slab samples its own region at its own offset, so no two blocks repeat. |

Generated in the shaders (no files): the coursed ashlar and its hairline joints, the 13 voussoirs of every
arch, the eased arrises, the running-bond paving with each slab a fraction of a degree out of level, the
sky gradient and the haze. The print's grain is an inline SVG `feTurbulence` in the page's CSS.

The shadows are depth maps drawn by the scene: the near arcade 4096², the far arcade 2048², and for the
ensemble an atlas of 4096² (all halved on phones and machines with four cores or fewer) holding up to eight tiles,
each fitted tightly around a run of places as the sun sees them, so one texel covers 1–3 mm of the casters
(v1: a single 4096 × 2048 map, up to 8.2 mm). All are sampled with percentage-closer soft shadows for the real
sun, 0.53° across: the penumbra on each side is the blocker's distance × tan 0.265° (v1 used tan 0.5°, nearly
twice as soft), so a shadow is sharp where a leg meets the stone and softens along its length. Checked against
the ground truth, 64 hard shadows averaged over the sun's disc: mean difference 0.9/255. When the picture holds
still, up to eight frames (four on the smaller setting) are averaged, each with the kernels turned and the
pixel grid moved by a sub-pixel (Halton) step, so penumbrae settle smooth and thin shadows are antialiased.

## Fonts (concept only)

| File | Family | Source | Licence | Production face to license |
|---|---|---|---|---|
| `fonts/jost-latin.woff2` | Jost v20, variable 100–900, latin subset | Google Fonts, https://fonts.gstatic.com/s/jost/v20/92zatBhPNqw73oTd4g.woff2 | SIL OFL 1.1 (`fonts/OFL-Jost.txt`) | Futura PT (ParaType) or Neue Plak (Monotype) |
| `fonts/eb-garamond-italic-latin.woff2` | EB Garamond Italic v33, variable 400–800, latin subset | Google Fonts, https://fonts.gstatic.com/s/ebgaramond/v33/SlGWmQSNjdsmc35JDF1K5GRweDs1Zw.woff2 | SIL OFL 1.1 (`fonts/OFL-EBGaramond.txt`) | Garamond Premier Italic (Adobe) or GT Super Text Italic (Grilli Type) |

EB Garamond sets one line only: the caption "An open chair."

## The stills

Every image in `img/` is a frame of this scene, rendered headless (Playwright Chromium, SwiftShader WebGL 2)
through `window.__renderAt(p)` with the page's own type hidden (`?capture`), at device pixel ratio 1, with the
full eight-frame accumulation. Encoded with sharp: AVIF q56 (effort 4), WebP q82, JPEG (mozjpeg) q82.

| Files | Frame | p | Sun (azimuth, elevation) |
|---|---|---|---|
| `portico-hero-wide.*` (2560 × 1600), `portico-hero-wide-1600.*` (1600 × 1000) | the first frame, wide | 0 | 166°, 21° |
| `portico-hero-tall.*` (1170 × 2535) | the first frame, tall (phones) | 0 | 166°, 21° |
| `portico-trio-wide.*`, `-quartet-`, `-chamber-`, `-full-` (1600 × 1000) | the four ensembles, for reduced motion and no WebGL | 0.53, 0.65, 0.77, 0.95 | 160°, 8° |
| `portico-open-chair.*` (1200 × 1500) | For musicians: one chair and its music stand, no instrument (its own lens) | 0.42 | 160°, 8° |

## Byte budget

What a first visit transfers before the scene is live (fonts and images are counted as stored; text as gzip):

| | raw | over the wire |
|---|---|---|
| three.js (core + module) | 751 kB | 188 kB |
| scene code (`stage.js`, `portico.js`, `ensemble.js`) | 116 kB | 39 kB |
| travertine texture | 323 kB | 323 kB |
| page (HTML + CSS) and Jost | 59 kB | 36 kB |
| hero still: wide 2560 AVIF (a 2× desktop; 1× screens take the 24 kB 1600) / tall AVIF | 48 kB / 36 kB | 48 kB / 36 kB |
| **First view, desktop** | 1.30 MB | **0.63 MB** (limit 4 MB) |
| **First view, phone** | 1.29 MB | **0.62 MB** |
| **Every file in `assets/` plus the page** (all formats of all stills) | 2.47 MB | **1.81 MB** (limit 12 MB) |

Not in the first view: EB Garamond Italic (48 kB; requested with the page's layout for its one caption), the
open-chair still (28 kB AVIF, lazy), and the four ensemble stills (25-27 kB AVIF each, lazy, and only fetched in the
reduced-motion / no-WebGL view). Grand total for a full visit on a 2× desktop: about 0.71 MB.
No file name or folder starts with `_`; every file has an extension the host serves.
