// AFTERLIGHT — Film 5 · Past the Red // Copperplate engraving idiom: sepia ink hatching on plate paper. The spectrum is the only colour. (function () { const INK = '#231b14', PAPER = '#efe5cf', GLASS = '#f6f0e2'; // ---------- deterministic helpers ---------- function rng(seed) { let a = seed >>> 0; return () => { a = (a + 0x6D2B79F5) >>> 0; let t = a; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } function P(pts) { const p = new Path2D(); p.moveTo(pts[0][0], pts[0][1]); for (let i = 1; i < pts.length; i++) p.lineTo(pts[i][0], pts[i][1]); p.closePath(); return p; } function bbOf(pts) { let a = 1e9, b = 1e9, c = -1e9, d = -1e9; pts.forEach(q => { a = Math.min(a, q[0]); b = Math.min(b, q[1]); c = Math.max(c, q[0]); d = Math.max(d, q[1]); }); return [a, b, c, d]; } function begin(ctx, w, h, seed) { ctx.save(); ctx.scale(w / 1280, h / 720); ctx.fillStyle = PAPER; ctx.fillRect(0, 0, 1280, 720); const R = rng(seed); ctx.fillStyle = 'rgba(120,90,50,0.06)'; for (let i = 0; i < 900; i++) ctx.fillRect(R() * 1280, R() * 720, 1 + R() * 2, 1); return R; } function end(ctx) { // plate mark ctx.strokeStyle = 'rgba(35,27,20,0.6)'; ctx.lineWidth = 2; ctx.strokeRect(12, 12, 1256, 696); ctx.restore(); } // parallel engraved lines clipped to a region function hatch(ctx, R, path, ang, sp, lw, alpha, bb) { bb = bb || [0, 0, 1280, 720]; ctx.save(); ctx.clip(path); ctx.strokeStyle = INK; ctx.globalAlpha = alpha == null ? 0.8 : alpha; ctx.lineWidth = lw; const cx = (bb[0] + bb[2]) / 2, cy = (bb[1] + bb[3]) / 2, D = Math.hypot(bb[2] - bb[0], bb[3] - bb[1]) / 2 + 6; const dx = Math.cos(ang), dy = Math.sin(ang), nx = -dy, ny = dx; ctx.beginPath(); for (let d = -D; d <= D; d += sp) { const j = (R() - 0.5) * sp * 0.25, px = cx + nx * (d + j), py = cy + ny * (d + j); ctx.moveTo(px - dx * D, py - dy * D); for (let s = 1; s <= 3; s++) { const t = -D + 2 * D * s / 3, wb = (R() - 0.5) * 0.9; ctx.lineTo(px + dx * t + nx * wb, py + dy * t + ny * wb); } } ctx.stroke(); ctx.restore(); } // tonal value 0..4 by layered hatching function tone(ctx, R, path, lvl, bb, a0, sp) { a0 = a0 == null ? -0.6 : a0; sp = sp || 6; const angs = [a0, a0 + 1.35, a0 + 0.7, a0 + 2.05]; for (let i = 0; i < Math.min(lvl, 4); i++) hatch(ctx, R, path, angs[i], sp * (i ? 1.15 : 1), i ? 0.7 : 0.9, 0.78, bb); } function solid(ctx, R, pts, lvl, a0, sp, outline) { const p = P(pts); ctx.fillStyle = PAPER; ctx.fill(p); if (lvl) tone(ctx, R, p, lvl, bbOf(pts), a0, sp); if (outline !== false) { ctx.strokeStyle = INK; ctx.lineWidth = outline || 1.4; ctx.stroke(p); } return p; } function rays(ctx, R, path, from, to, n, alpha) { ctx.save(); ctx.clip(path); ctx.strokeStyle = INK; ctx.globalAlpha = alpha; ctx.lineWidth = 0.6; ctx.beginPath(); for (let i = 0; i < n; i++) { const t = R(); const a = [from[0][0] + (from[1][0] - from[0][0]) * t, from[0][1] + (from[1][1] - from[0][1]) * t]; const b = [to[0][0] + (to[1][0] - to[0][0]) * t, to[0][1] + (to[1][1] - to[0][1]) * t]; ctx.moveTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); } ctx.stroke(); ctx.restore(); } // the only colour: a prismatic band, violet at x0, red ending at x1 (soft red falloff) function spectrum(ctx, R, path, x0, x1, alpha) { const g = ctx.createLinearGradient(x0, 0, x1, 0); g.addColorStop(0.00, 'rgba(92,58,140,0)'); g.addColorStop(0.05, 'rgba(92,58,140,1)'); g.addColorStop(0.18, 'rgba(52,86,168,1)'); g.addColorStop(0.38, 'rgba(63,150,80,1)'); g.addColorStop(0.56, 'rgba(232,206,58,1)'); g.addColorStop(0.73, 'rgba(229,138,43,1)'); g.addColorStop(0.90, 'rgba(196,38,42,1)'); g.addColorStop(0.97, 'rgba(160,28,34,0.85)'); g.addColorStop(1.00, 'rgba(160,28,34,0)'); ctx.save(); ctx.clip(path); ctx.globalAlpha = alpha == null ? 0.88 : alpha; ctx.fillStyle = g; ctx.fill(path); ctx.restore(); // engraved horizontal lines laid over the colour, so it stays inside the plate hatch(ctx, R, path, 0.0, 3.2, 0.5, 0.16, null); } // thermometer: ball centre (x,y), tube direction ang, length len, mercury fraction lvl function thermo(ctx, R, x, y, ang, len, lvl, o) { o = o || {}; const r = o.r || 9, tw = o.tw || r * 0.55, bw = o.bw || r * 1.9, tk = o.tick || r * 0.8; ctx.save(); ctx.translate(x, y); ctx.rotate(ang); const bp = new Path2D(); bp.rect(r + 5, -bw / 2, len - r - 5, bw); ctx.fillStyle = PAPER; ctx.fill(bp); tone(ctx, R, bp, o.boardTone == null ? 1 : o.boardTone, [-10, -bw, len + 10, bw], 0.25, Math.max(2.2, r * 0.32)); ctx.strokeStyle = INK; ctx.lineWidth = Math.max(0.8, r * 0.1); ctx.stroke(bp); ctx.beginPath(); let k = 0; for (let t = r + 14; t < len - 6; t += tk, k++) { const big = k % 5 === 0; ctx.moveTo(t, -bw / 2); ctx.lineTo(t, -bw / 2 + (big ? bw * 0.38 : bw * 0.2)); } ctx.lineWidth = Math.max(0.6, r * 0.06); ctx.stroke(); ctx.fillStyle = GLASS; ctx.fillRect(0, -tw / 2, len + 8, tw); ctx.strokeStyle = INK; ctx.lineWidth = Math.max(0.7, r * 0.08); ctx.strokeRect(0, -tw / 2, len + 8, tw); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(r + (len - r) * lvl, 0); ctx.lineWidth = tw * 0.5; ctx.strokeStyle = INK; ctx.stroke(); ctx.beginPath(); ctx.arc(0, 0, r, 0, 7); ctx.fillStyle = '#15100b'; ctx.fill(); ctx.beginPath(); ctx.arc(-r * 0.3, -r * 0.38, r * 0.2, 0, 7); ctx.fillStyle = 'rgba(239,229,207,0.55)'; ctx.fill(); ctx.restore(); } // small inclined plane under a thermometer (screen coords) function wedge(ctx, R, x, y, ang, r, L) { const yb = y + r + 1, xb = x + Math.cos(ang) * L, yc = y + Math.sin(ang) * L + r * 1.1; solid(ctx, R, [[x + r * 0.6, yb], [xb, yb], [xb, yc]], 2, 1.2, Math.max(2.5, r * 0.35), Math.max(0.8, r * 0.1)); } // head in profile facing left, local unit ~100 tall function head(ctx, R, x, y, s, tilt) { ctx.save(); ctx.translate(x, y); ctx.rotate(tilt || 0); ctx.scale(s, s); const lw = 1.5 / Math.max(s, 0.6); const H = new Path2D('M18,-58 C-8,-64 -28,-52 -31,-30 L-33,-17 L-45,1 L-35,7 L-37,14 L-34,19 L-36,24 Q-36,37 -23,41 Q-6,45 8,33 L12,58 L38,58 L38,26 Q52,6 48,-22 Q44,-52 18,-58 Z'); ctx.fillStyle = PAPER; ctx.fill(H); const shade = P([[0, -70], [60, -70], [60, 60], [-8, 60], [-14, 20]]); ctx.save(); ctx.clip(H); hatch(ctx, R, shade, -0.9, 3.2, 0.6, 0.6, [-10, -70, 60, 60]); ctx.restore(); ctx.strokeStyle = INK; ctx.lineWidth = lw; ctx.stroke(H); // powdered hair, tied back const hair = new Path2D('M-28,-38 Q-22,-64 16,-62 Q48,-56 51,-20 Q53,6 42,26 Q36,10 31,-4 Q22,-28 -4,-38 Q-17,-43 -28,-38 Z'); ctx.fillStyle = PAPER; ctx.fill(hair); ctx.save(); ctx.clip(hair); ctx.strokeStyle = INK; ctx.globalAlpha = 0.55; ctx.lineWidth = 0.7 / Math.max(s, 0.6); ctx.beginPath(); for (let i = 0; i < 16; i++) { const o = i * 4.2 - 30; ctx.moveTo(-26 + o * 0.2, -44 + o * 0.1); ctx.quadraticCurveTo(20 + o * 0.3, -64 + o, 50, -16 + o * 0.8); } ctx.stroke(); ctx.restore(); ctx.strokeStyle = INK; ctx.lineWidth = lw; ctx.stroke(hair); ctx.beginPath(); ctx.ellipse(30, -8, 8, 12, 0.2, 0, 7); ctx.fillStyle = PAPER; ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.ellipse(30, -8, 4, 7, 0.2, 0, 7); ctx.stroke(); ctx.fillStyle = '#15100b'; ctx.fill(new Path2D('M40,26 L52,18 L50,34 Z M40,26 L30,40 L44,38 Z')); // features ctx.beginPath(); ctx.moveTo(-31, -24); ctx.quadraticCurveTo(-22, -28, -12, -23); // brow ctx.moveTo(-28, -14); ctx.quadraticCurveTo(-22, -17, -16, -13); // upper lid ctx.moveTo(-36, 19); ctx.lineTo(-29, 19); // mouth ctx.moveTo(-36, 5); ctx.quadraticCurveTo(-32, 3, -30, 6); // nostril ctx.moveTo(-24, 2); ctx.quadraticCurveTo(-16, 14, -22, 28); // cheek ctx.moveTo(-6, 30); ctx.quadraticCurveTo(4, 26, 8, 33); // jaw ctx.stroke(); ctx.beginPath(); ctx.arc(-24, -13.5, 1.8, 0, 7); ctx.fillStyle = INK; ctx.fill(); ctx.restore(); } // hands, fingers along +x. kind: 'fist' (gripping across a tube) or 'open' function hand(ctx, R, x, y, ang, s, kind, flip) { ctx.save(); ctx.translate(x, y); ctx.rotate(ang); ctx.scale(s, flip ? -s : s); const lw = 1.5 / s; // lace cuff const cuff = new Path2D('M-16,-22 L-2,-18 L-6,-12 L0,-6 L-5,0 L0,6 L-6,12 L-2,18 L-16,22 Z'); ctx.fillStyle = PAPER; ctx.fill(cuff); ctx.strokeStyle = INK; ctx.lineWidth = lw * 0.8; ctx.stroke(cuff); if (kind === 'fist') { const f = new Path2D('M0,-15 Q14,-19 28,-17 Q42,-16 46,-6 Q49,6 44,15 Q36,22 22,21 Q8,20 0,15 Z'); ctx.fillStyle = PAPER; ctx.fill(f); hatch(ctx, R, f, 0.3, 2.6 / s * 1.4, 0.5 / s, 0.55, [0, 0, 50, 22]); ctx.lineWidth = lw; ctx.strokeStyle = INK; ctx.stroke(f); ctx.beginPath(); for (let i = 0; i < 3; i++) { const yy = -8 + i * 8; ctx.moveTo(30, yy); ctx.quadraticCurveTo(40, yy - 1, 47, yy + 1); } ctx.lineWidth = lw * 0.8; ctx.stroke(); const th = new Path2D('M6,-15 Q20,-31 40,-25 Q47,-22 44,-17 Q30,-15 16,-11 Z'); ctx.fillStyle = PAPER; ctx.fill(th); ctx.lineWidth = lw; ctx.stroke(th); ctx.beginPath(); ctx.ellipse(40, -21, 3.2, 2.2, -0.2, 0, 7); ctx.lineWidth = lw * 0.6; ctx.stroke(); } else { const palm = new Path2D('M0,-15 Q16,-19 32,-16 L35,15 Q16,19 0,15 Z'); ctx.fillStyle = PAPER; ctx.fill(palm); hatch(ctx, R, palm, 0.4, 3.4, 0.5 / s, 0.5, [0, -20, 36, 20]); ctx.strokeStyle = INK; ctx.lineWidth = lw; ctx.stroke(palm); const fingers = [[-12, 33, -0.10], [-3, 39, -0.02], [6, 36, 0.06], [13, 27, 0.16]]; fingers.forEach(([yy, L, a]) => { ctx.save(); ctx.translate(31, yy); ctx.rotate(a); const fp = new Path2D(); fp.moveTo(0, -4); fp.lineTo(L * 0.55, -3.8); fp.quadraticCurveTo(L * 0.8, -2, L, 0); fp.quadraticCurveTo(L * 0.85, 4, L * 0.55, 3.8); fp.lineTo(0, 4); fp.closePath(); ctx.fillStyle = PAPER; ctx.fill(fp); ctx.lineWidth = lw; ctx.stroke(fp); ctx.beginPath(); ctx.moveTo(L * 0.5, -3); ctx.lineTo(L * 0.5, 3); ctx.lineWidth = lw * 0.5; ctx.stroke(); ctx.restore(); }); ctx.save(); ctx.translate(10, -15); ctx.rotate(-0.75); const tp = new Path2D('M0,-5 L20,-5 Q30,-3 30,0 Q28,5 20,5 L0,5 Z'); ctx.fillStyle = PAPER; ctx.fill(tp); ctx.lineWidth = lw; ctx.stroke(tp); ctx.restore(); } ctx.restore(); } function sleeve(ctx, R, pts, lvl) { return solid(ctx, R, pts, lvl == null ? 3 : lvl, -0.9, 4.5, 1.6); } // ======================= KEYFRAMES ======================= function k1(ctx, w, h) { const R = begin(ctx, w, h, 501); tone(ctx, R, P([[0, 0], [1280, 0], [1280, 720], [0, 720]]), 3, null, -0.55, 6.5); // window: dark green curtain (rendered in ink), small bright aperture with the prism solid(ctx, R, [[60, 40], [280, 40], [280, 380], [60, 380]], 4, 1.57, 4, 2); ctx.strokeStyle = 'rgba(239,229,207,0.35)'; ctx.lineWidth = 2; ctx.beginPath(); for (let i = 0; i < 6; i++) { const x = 85 + i * 34; ctx.moveTo(x, 130); ctx.quadraticCurveTo(x + 8, 260, x - 4, 378); } ctx.stroke(); solid(ctx, R, [[104, 62], [236, 62], [236, 126], [104, 126]], 0, 0, 0, 2); hatch(ctx, R, P([[104, 62], [236, 62], [236, 126], [104, 126]]), 0, 9, 0.5, 0.25, [104, 62, 236, 126]); solid(ctx, R, [[140, 118], [198, 118], [169, 74]], 1, 0.9, 3, 1.6); // sunbeam leaving the prism toward the stand const beam = P([[160, 106], [186, 102], [838, 470], [556, 496]]); ctx.save(); ctx.globalAlpha = 0.82; ctx.fillStyle = PAPER; ctx.fill(beam); ctx.restore(); rays(ctx, R, beam, [[160, 106], [186, 102]], [[556, 496], [838, 470]], 26, 0.22); // table solid(ctx, R, [[300, 430], [1200, 430], [1270, 560], [230, 560]], 2, 0.05, 5); solid(ctx, R, [[230, 560], [1270, 560], [1270, 602], [230, 602]], 4, 1.2, 4); solid(ctx, R, [[262, 602], [292, 602], [292, 720], [262, 720]], 3, 1.57, 4); solid(ctx, R, [[1200, 602], [1230, 602], [1230, 720], [1200, 720]], 3, 1.57, 4); // the stand: white paper with ruled lines const st = [[520, 452], [1000, 452], [1032, 520], [498, 520]]; solid(ctx, R, [[498, 520], [1032, 520], [1032, 532], [498, 532]], 3, 0, 3); const sp = solid(ctx, R, st, 1, 0.1, 7, 1.4); ctx.strokeStyle = 'rgba(35,27,20,0.45)'; ctx.lineWidth = 0.8; ctx.beginPath(); for (let i = 1; i < 5; i++) { const y = 452 + i * 13.6; ctx.moveTo(520 - i * 4.4, y); ctx.lineTo(1000 + i * 6.4, y); } ctx.stroke(); spectrum(ctx, R, P([[556, 470], [838, 470], [842, 500], [550, 500]]), 548, 842); // three thermometers resting at the right, in shade, on inclined planes [[930, 486], [950, 500], [970, 514]].forEach((q, i) => { wedge(ctx, R, q[0], q[1], -0.32, 5, 60); thermo(ctx, R, q[0], q[1], -0.32, 100, 0.3, { r: 5, boardTone: 1 }); }); // notebook solid(ctx, R, [[1060, 470], [1160, 466], [1176, 506], [1070, 512]], 0, 0, 0, 1.2); ctx.strokeStyle = 'rgba(35,27,20,0.4)'; ctx.beginPath(); for (let i = 0; i < 5; i++) { ctx.moveTo(1068, 478 + i * 6.5); ctx.lineTo(1162, 474 + i * 6.5); } ctx.stroke(); // the experimenter, seated behind the table, facing the light solid(ctx, R, [[1010, 312], [1180, 300], [1222, 430], [990, 430]], 3, -0.9, 4.5, 1.6); solid(ctx, R, [[1050, 306], [1092, 304], [1080, 352]], 0, 0, 0, 1.2); // cravat head(ctx, R, 1080, 248, 0.9, 0.05); sleeve(ctx, R, [[1010, 330], [1050, 320], [1080, 436], [1046, 446]]); hand(ctx, R, 1056, 452, -0.25, 0.75, 'open'); sleeve(ctx, R, [[1150, 330], [1190, 330], [1170, 460], [1130, 456]]); hand(ctx, R, 1146, 470, -1.6, 0.75, 'fist'); end(ctx); } function k2(ctx, w, h) { const R = begin(ctx, w, h, 502); tone(ctx, R, P([[0, 0], [1280, 0], [1280, 240], [0, 240]]), 3, null, -0.55, 6); solid(ctx, R, [[0, 240], [1280, 240], [1280, 720], [0, 720]], 2, 0.08, 5, false); // beam entering from upper left const beam = P([[230, 0], [330, 0], [800, 398], [232, 404]]); ctx.save(); ctx.globalAlpha = 0.7; ctx.fillStyle = PAPER; ctx.fill(beam); ctx.restore(); rays(ctx, R, beam, [[230, 0], [330, 0]], [[232, 404], [800, 398]], 30, 0.18); // stand paper solid(ctx, R, [[60, 556], [1240, 556], [1240, 576], [60, 576]], 3, 0, 3); solid(ctx, R, [[150, 330], [1150, 330], [1240, 556], [60, 556]], 1, 0.12, 7.5, 1.6); ctx.strokeStyle = 'rgba(35,27,20,0.42)'; ctx.lineWidth = 1; ctx.beginPath(); for (let i = 1; i < 6; i++) { const t = i / 6, y = 330 + 226 * t; ctx.moveTo(150 - 90 * t, y); ctx.lineTo(1150 + 90 * t, y); } ctx.stroke(); spectrum(ctx, R, P([[232, 396], [800, 392], [812, 476], [214, 482]]), 205, 812); // two resting thermometers off the paper, in shade (the standards) thermo(ctx, R, 690, 640, -0.12, 330, 0.3, { r: 8, boardTone: 2 }); thermo(ctx, R, 700, 680, -0.12, 330, 0.3, { r: 8, boardTone: 2 }); // No.1 moved through the colours, ball in the yellow wedge(ctx, R, 560, 438, -0.55, 11, 130); thermo(ctx, R, 560, 438, -0.55, 360, 0.38, { r: 11 }); // the experimenter's hand gripping the tube, sleeve from upper right sleeve(ctx, R, [[1280, 60], [1280, 150], [858, 300], [832, 236]]); hand(ctx, R, 828, 262, 2.6, 1.05, 'fist'); // writing hand on the notebook, lower right solid(ctx, R, [[900, 600], [1260, 590], [1280, 720], [920, 720]], 0, 0, 0, 1.6); ctx.strokeStyle = 'rgba(35,27,20,0.4)'; ctx.lineWidth = 0.9; ctx.beginPath(); for (let i = 0; i < 6; i++) { ctx.moveTo(918, 618 + i * 16); ctx.lineTo(1262, 608 + i * 16); } ctx.stroke(); sleeve(ctx, R, [[1280, 520], [1280, 610], [1150, 660], [1132, 610]]); ctx.strokeStyle = INK; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(1060, 680); ctx.lineTo(1150, 560); ctx.stroke(); // quill ctx.beginPath(); ctx.moveTo(1150, 560); ctx.quadraticCurveTo(1185, 520, 1176, 490); ctx.quadraticCurveTo(1160, 540, 1150, 560); ctx.fillStyle = PAPER; ctx.fill(); ctx.lineWidth = 1; ctx.stroke(); hand(ctx, R, 1130, 640, 3.3, 0.95, 'fist', true); end(ctx); } function k3(ctx, w, h) { const R = begin(ctx, w, h, 503); tone(ctx, R, P([[0, 0], [1280, 0], [1280, 360], [0, 360]]), 4, null, -0.5, 6); // stand paper seen at table level const paper = [[0, 372], [1280, 340], [1280, 720], [0, 720]]; solid(ctx, R, paper, 1, 0.05, 8, false); ctx.strokeStyle = INK; ctx.lineWidth = 1.6; ctx.beginPath(); ctx.moveTo(0, 372); ctx.lineTo(1280, 340); ctx.stroke(); ctx.strokeStyle = 'rgba(35,27,20,0.42)'; ctx.lineWidth = 1; ctx.beginPath(); [395, 435, 495, 590].forEach((y, i) => { ctx.moveTo(0, y + 4); ctx.lineTo(1280, y - 26 + i * 4); }); [[140, 370], [760, 356], [1180, 346]].forEach(([x, y]) => { ctx.moveTo(x, y); ctx.lineTo(x + (x - 640) * 1.5, 720); }); ctx.stroke(); // the visible spectrum ends; past it, the same paper spectrum(ctx, R, P([[0, 410], [480, 396], [560, 720], [0, 720]]), -820, 560); // No.2 resting beyond the red on its inclined plane wedge(ctx, R, 720, 560, -0.42, 18, 240); thermo(ctx, R, 720, 560, -0.42, 640, 0.34, { r: 18 }); // hand withdrawing, sleeve to upper right sleeve(ctx, R, [[1280, 120], [1280, 220], [1130, 300], [1100, 236]]); hand(ctx, R, 1094, 278, 2.55, 1.25, 'open'); end(ctx); } function k4(ctx, w, h) { const R = begin(ctx, w, h, 504); tone(ctx, R, P([[0, 0], [1280, 0], [1280, 720], [0, 720]]), 3, null, -0.55, 6); // a lighter fall of window light behind the head const glow = P([[0, 0], [520, 0], [260, 720], [0, 720]]); ctx.save(); ctx.globalAlpha = 0.45; ctx.fillStyle = PAPER; ctx.fill(glow); ctx.restore(); // table edge and a sliver of the spectrum on the stand, lower left solid(ctx, R, [[0, 620], [560, 610], [600, 720], [0, 720]], 1, 0.05, 7, 1.4); spectrum(ctx, R, P([[0, 640], [300, 632], [320, 720], [0, 720]]), -300, 322); // coat solid(ctx, R, [[470, 380], [960, 360], [1150, 720], [380, 720]], 3, -0.9, 4.5, 1.8); solid(ctx, R, [[560, 372], [700, 368], [640, 520]], 0, 0, 0, 1.4); // cravat solid(ctx, R, [[700, 368], [760, 372], [690, 560], [640, 520]], 4, -0.4, 4, 1.4); // lapel shadow head(ctx, R, 640, 220, 2.25, 0.2); // two thermometers held upright side by side: No.2 (from past the red) and No.3 (the shaded standard) thermo(ctx, R, 300, 600, -Math.PI / 2, 470, 0.64, { r: 15, boardTone: 1 }); thermo(ctx, R, 400, 612, -Math.PI / 2, 470, 0.44, { r: 15, boardTone: 2 }); sleeve(ctx, R, [[520, 420], [600, 440], [400, 560], [352, 512]]); hand(ctx, R, 342, 540, 3.1, 1.15, 'fist'); sleeve(ctx, R, [[560, 520], [640, 560], [490, 640], [450, 590]]); hand(ctx, R, 440, 612, 3.0, 1.15, 'fist', true); end(ctx); } function k5(ctx, w, h) { const R = begin(ctx, w, h, 505); tone(ctx, R, P([[0, 0], [1280, 0], [1280, 330], [0, 330]]), 4, null, -0.5, 6); const paper = [[0, 350], [1280, 318], [1280, 720], [0, 720]]; solid(ctx, R, paper, 1, 0.05, 8, false); ctx.strokeStyle = INK; ctx.lineWidth = 1.6; ctx.beginPath(); ctx.moveTo(0, 350); ctx.lineTo(1280, 318); ctx.stroke(); ctx.strokeStyle = 'rgba(35,27,20,0.35)'; ctx.lineWidth = 1; ctx.beginPath(); [420, 520, 660].forEach((y, i) => { ctx.moveTo(0, y); ctx.lineTo(1280, y - 30 + i * 6); }); ctx.stroke(); // only the last edge of the red, at the left margin spectrum(ctx, R, P([[0, 360], [70, 358], [120, 720], [0, 720]]), -1150, 120, 0.8); // shade cast across the standard, back right const sh = P([[880, 326], [1280, 316], [1280, 720], [1010, 720]]); tone(ctx, R, sh, 2, [880, 316, 1280, 720], 0.9, 5); thermo(ctx, R, 1060, 520, -1.3, 400, 0.42, { r: 16, boardTone: 2 }); // No.2 in the empty patch: the column stands higher wedge(ctx, R, 470, 610, -1.08, 30, 220); thermo(ctx, R, 470, 610, -1.08, 700, 0.72, { r: 30, tick: 15 }); end(ctx); } // ======================= DATA ======================= const SRC = { p1: { kind: 'research paper', title: 'Investigation of the Powers of the prismatic Colours to heat and illuminate Objects (Phil. Trans. 90, pp. 255-283)', creator: 'William Herschel', date: '1800 (read 27 March 1800)', url: 'https://web.physics.ucsb.edu/~lecturedemonstrations/Linked%20files/Phil%20Trans%201800%20pp%20255-283.pdf', rights: 'public domain' }, p2: { kind: 'research paper', title: 'Experiments on the Refrangibility of the Invisible Rays of the Sun (Phil. Trans. 90, pp. 284-292)', creator: 'William Herschel', date: '1800', url: 'https://web.physics.ucsb.edu/~lecturedemonstrations/Linked%20files/Phil%20Trans%201800%20pp%20284-292.pdf', rights: 'public domain' }, p2j: { kind: 'source text', title: 'Experiments on the Refrangibility of the Invisible Rays of the Sun — OCR text (JSTOR 107057)', creator: 'William Herschel; OCR corpus by Jim Clifford', date: '1800', url: 'https://jimclifford.ca/philosophical-transactions-ocr-1665-1869/articles/jstor-107057/', rights: 'public domain' }, plate: { kind: 'still image', title: 'Plate X, p. 282, Phil. Trans. 1800 — Herschel\'s apparatus (as reproduced by Linda Hall Library, Scientist of the Day)', creator: 'William Herschel; Linda Hall Library', date: '1800', url: 'https://www.lindahall.org/about/news/scientist-of-the-day/william-herschel-4/', rights: 'public domain' }, abbott: { kind: 'still image', title: 'Sir William Herschel (oil on canvas, NPG 98), Wikimedia Commons file William_Herschel01.jpg', creator: 'Lemuel Francis Abbott', date: '1785', url: 'https://en.wikipedia.org/wiki/File:William_Herschel01.jpg', rights: 'public domain' }, crayon: { kind: 'museum object', title: 'Portrait of Sir William Herschel (crayon copy after Abbott), Science Museum Group co66185', creator: 'C. Shorland after L. F. Abbott', date: '1785-1958', url: 'https://collection.sciencemuseumgroup.org.uk/objects/co66185/portrait-of-sir-william-herschel-drawing-portrait', rights: 'CC BY-NC-SA 4.0' }, therm: { kind: 'museum object', title: 'Mercury in glass thermometer, Science Museum Group 1927-1708', creator: 'unknown maker', date: '1780-1840', url: 'https://collection.sciencemuseumgroup.org.uk/objects/co2269/mercury-in-glass-thermometer', rights: 'CC BY-NC-SA 4.0' }, opticks: { kind: 'still image', title: 'M0010896: Production of spectrum by refraction at a prism (diagram from Newton, Opticks, 1721)', creator: 'Wellcome Collection', date: '1948 negative of a 1721 plate', url: 'https://wellcomecollection.org/works/brdptc68', rights: 'public domain' }, lightrays: { kind: 'archive footage', title: 'Experiments with Light Rays (Prelinger Archives)', creator: 'Bray Studios, Inc.', date: 'undated (uploaded 2012)', url: 'https://archive.org/details/6026_Experiments_with_Light_Rays_01_52_34_17', rights: 'unknown — not granted' }, solar: { kind: 'archive footage', title: 'Solar and Terrestrial Radiation (Prelinger Archives)', creator: 'Handy (Jam) Organization for the U.S. Air Force', date: '1950', url: 'https://archive.org/details/4122_Solar_And_Terrestrial_Radiation', rights: 'unknown — not granted' }, tfh: { kind: 'archive footage', title: 'Twenty-Four Hours of Progress (Part I) — chemistry-lab CU of a hand holding a thermometer in a boiling vial', creator: 'Film Counselors, Inc. for the Oil Industry Information Committee', date: '1950', url: 'https://archive.org/details/TwentyFo1950', rights: 'public domain' }, research: { kind: 'archive footage', title: '[Research Work] — CU thermometer on brick wall, column falling from over 80 to minus 10', creator: 'General Motors Research Laboratories (Handy (Jam) Organization, unverified)', date: 'ca. 1930s', url: 'https://archive.org/details/6099_Research_Work_00_44_01_00', rights: 'unknown — not granted' }, ipac: { kind: 'source text', title: 'Herschel Infrared Experiment', creator: 'IPAC / Caltech (NASA education)', date: 'n.d.', url: 'https://www.ipac.caltech.edu/Outreach/Edu/Herschel/experiment.html', rights: 'unknown — not granted' }, }; function ref(key, use) { return Object.assign({}, SRC[key], { use: use, verified: true }); } window.AFTERLIGHT_FILMS.push({ id: 'F5', n: 5, title: 'Past the Red', role: 'The scientific foundation', understand: 'What the eye cannot see can still produce an effect; an instrument changes what becomes knowable.', medium: 'Copperplate engraving and etching (sepia line hatching on plate paper, plate-mark border) with the prismatic spectrum as the only colour: it is the visual language of the 1800 Philosophical Transactions plates, and because only the visible band is coloured, the region past the red stays plain paper while the instrument registers it.', archivePlan: 'COMPOSED SCENE · archive fragments re-performed · not a recorded event. The room, Herschel, the stand and the thermometers are INVENTED engraved cut-paper layers built from his 1800 descriptions and Plate X; the coloured band is a PHOTOGRAPHED matte lifted from prism footage (Bray, Experiments with Light Rays) and laid into the engraving; the hand-on-thermometer gestures and the moving mercury column are PHOTOGRAPHED rotoscope sources from Prelinger lab footage, redrawn in line. Paper-slip captions quote Herschel (\"No. 3 never differed much from No. 2\") and mark each layer PHOTOGRAPHED or INVENTED; no slip, overlay or colour is ever placed in the patch past the red.', basis: [ { title: 'Herschel, Investigation of the Powers of the prismatic Colours to heat and illuminate Objects, Phil. Trans. 90 (1800) 255-283', url: SRC.p1.url, note: 'Prism set in the upper part of an open window; spectrum thrown on a table; pasteboard with an opening a little larger than a thermometer ball; three thermometers on small inclined planes, "their balls were blacked with Japan ink"; one exposed, the other two kept "under the shade of the pasteboard" as standards. Plate X shows the apparatus.', verified: true }, { title: 'Herschel, Experiments on the Refrangibility of the Invisible Rays of the Sun, Phil. Trans. 90 (1800) 284-292', url: SRC.p2.url, note: 'Second series: window darkened with a thick dark green curtain; a small stand covered with white paper ruled in lines; thermometers No. 1-3 moved deliberately beyond the visible red (and violet); No. 3 kept as standard and positions swapped; heating maximum placed "probably not less than half an inch beyond the last visible" red. Numerical readings in the OCR are garbled and are not used in the film.', verified: true }, { title: 'Same paper, OCR text page (JSTOR stable 107057)', url: SRC.p2j.url, note: 'Confirms title, author, year; the JSTOR page itself (https://www.jstor.org/stable/107057) would not load (client challenge), so it is cited through this OCR corpus.', verified: true }, { title: 'Herschel, Experiments on the Solar, and on the Terrestrial Rays that Occasion Heat, Part I (JSTOR 107058)', url: 'https://jimclifford.ca/philosophical-transactions-ocr-1665-1869/articles/jstor-107058/', note: 'Companion paper of 1800 comparing rays of heat and rays of light; context only.', verified: true }, { title: 'Royal Society manuscript L&P/11/140, "On solar and terrestrial rays causing heat" [Continued], with a plate', url: 'https://makingscience.royalsociety.org/items/l-and-p_11_140', note: 'Herschel\'s handwritten paper, read 15 May 1800. Rights not stated.', verified: true }, { title: 'NASA/IPAC Herschel Infrared Experiment', url: SRC.ipac.url, note: 'Popular account: blackened bulbs, temperature rising toward the red and higher beyond it; states the classroom version differs from Herschel\'s procedure.', verified: true }, { title: 'NASA Space Place, Herschel infrared activity (PDF)', url: 'https://spaceplace.nasa.gov/review/posters/herschel/Herschel-ir-activity.pdf', note: 'States three thermometers with blackened bulbs, one in the colour and two beyond the spectrum as controls. That last detail does not match Herschel\'s first paper, where the two standards sat in the shade of the pasteboard.', verified: true }, { title: 'Royal Society Publishing DOI landing pages for Phil. Trans. vol. 90', url: 'https://royalsocietypublishing.org/doi/10.1098/rstl.1800.0015', note: 'Could not be opened (client error); the DOI-to-paper mapping is not confirmed, so this entry is not relied on.', verified: false }, ], keyframes: [ { n: 1, t: 0.0, title: 'The spectrum on the table', shot: 'WIDE, side-on, eye level; window and prism frame left, Herschel seated behind the table frame right', action: 'A narrow beam from a prism set high in a curtained window lays a spectrum across a ruled white stand on the table. Herschel sits behind the table with three thermometers resting on small inclined planes in the shade at his end.', mechanism: 'sun → prism (refracts, spreads by refrangibility) → paper (shows colours) ; thermometers → room (settle to the temperature of the place) ; colours ⇝ experimenter (look like the whole of the light)', layers: [ { name: 'room, curtained window, table, stand (engraved cut paper)', source: 'INVENTED', ref: 0 }, { name: 'spectrum band matte', source: 'PHOTOGRAPHED', ref: 1 }, { name: 'Herschel figure, costume and hair', source: 'INVENTED', ref: 2 }, { name: 'apparatus arrangement after Plate X', source: 'INVENTED', ref: 3 }, ], prompt: 'Wide side view of a dark Georgian room, 1800: a glass prism in the top of a curtained window sends a narrow sunbeam onto a small stand covered with ruled white paper on a wooden table, where a spectrum lies; an elderly man with powdered tied-back hair, dark coat, white cravat, sits behind the table beside three thermometers with black bulbs on tiny wooden ramps. Copperplate engraving, sepia cross-hatching, cream plate paper; the spectrum is the only colour. no text, no logos', draw: k1, refs: [ ref('p1', 'citation and staging: prism in the upper part of an open window, spectrum thrown on a table, three thermometers on small inclined planes'), ref('lightrays', 'matte source: the coloured band is lifted from a prism shot in this reel and keyed into the engraving'), ref('abbott', 'costume and likeness reference for Herschel (hair, cravat, coat)'), ref('plate', 'apparatus reference: layout of prism, slit/pasteboard and thermometers as engraved in 1800'), ], }, { n: 2, t: 14.0, title: 'Through the colours', shot: 'MS, high angle over the stand; beam entering top left; gripping hand frame right; writing hand and notebook lower right', action: 'Herschel slides thermometer No. 1 on its ramp from colour to colour, holding the ball in the yellow while his other hand waits on the notebook. The two standards lie off the paper, in shade.', mechanism: 'colour → blackened ball (absorbs) → mercury (expands) ; reading → notebook (recorded) ; standards ⊣ reading (subtract the room)', layers: [ { name: 'stand, ruled paper, table and notebook (engraved)', source: 'INVENTED', ref: 0 }, { name: 'spectrum band matte', source: 'PHOTOGRAPHED', ref: 2 }, { name: 'thermometer No. 1 drawn after a period instrument', source: 'INVENTED', ref: 1 }, { name: 'gripping hand, rotoscoped then engraved', source: 'PHOTOGRAPHED', ref: 3 }, ], prompt: 'High-angle medium shot of a ruled white paper stand on a dark table, a prismatic spectrum from violet to red across it; a man\'s hand in a dark sleeve with a lace cuff grips a long mercury thermometer with a black bulb resting on a small wooden ramp in the yellow band; a second hand with a quill over an open notebook at lower right. Copperplate engraving, sepia hatching, spectrum the only colour. no text, no logos', draw: k2, refs: [ ref('p1', 'citation: balls "blacked with Japan ink"; one thermometer exposed to a colour while the rest stay "under the shade of the pasteboard"'), ref('therm', 'object reference: proportions of a late-18th-century mercury-in-glass thermometer on a wooden scale board'), ref('lightrays', 'matte source: spectrum band'), ref('tfh', 'rotoscope reference: close-up of a hand holding a thermometer in a laboratory, redrawn in engraved line'), ], }, { n: 3, t: 28.0, title: 'Just outside the red', shot: 'CU at table level, low angle along the paper; red edge of the spectrum frame left, empty ruled paper frame right', action: 'Herschel sets a second thermometer on its ramp in the plain paper just past where the red ends, and lifts his hand away. Nothing on the paper marks the place.', mechanism: 'sun → prism → paper past the red (no visible change) ; blackened ball → mercury (?) ; edge of colour ⇝ experimenter (looks like the edge of the light)', layers: [ { name: 'paper surface and ruled lines (engraved)', source: 'INVENTED', ref: 0 }, { name: 'red edge of spectrum matte', source: 'PHOTOGRAPHED', ref: 2 }, { name: 'withdrawing hand', source: 'INVENTED', ref: null }, { name: 'thermometer No. 2 and ramp', source: 'INVENTED', ref: 1 }, ], prompt: 'Low table-level close-up across a sheet of ruled white paper in a dark room: on the left the orange and red end of a projected spectrum stops at a soft edge; on the right, in plain paper, a mercury thermometer with a black bulb lies on a small wooden ramp; an open hand with a lace cuff withdraws at upper right. Copperplate engraving, sepia hatching, only the spectrum in colour. no text, no logos', draw: k3, refs: [ ref('p2', 'citation: stand covered with white paper ruled in lines; thermometers moved along the lines beyond the visible red'), ref('p2j', 'source text for paper-slip captions quoting the beyond-red placements'), ref('lightrays', 'matte source: red end of the band'), ref('solar', 'archive footage for a cutaway or sound bed on radiant heat; it contains no shot of this experiment'), ], }, { n: 4, t: 40.0, title: 'Compared with the standard', shot: 'MCU, Herschel in profile frame right, two thermometers held upright frame left, sliver of the stand bottom left', action: 'He lifts the thermometer from past the red and holds it beside the shaded standard. Its column stands higher; he looks from one to the other, then sets it back in the same empty patch.', mechanism: 'standard ⊣ reading (the room is ruled out) ; difference ⇝ experimenter (something past the red heats) ; return → same patch (repeat, not a fluke)', layers: [ { name: 'Herschel head, coat, hands (engraved)', source: 'INVENTED', ref: 2 }, { name: 'the two thermometers', source: 'INVENTED', ref: 0 }, { name: 'hand grip on upright tube, rotoscoped', source: 'PHOTOGRAPHED', ref: 3 }, { name: 'spectrum sliver on the stand', source: 'PHOTOGRAPHED', ref: null }, ], prompt: 'Medium close-up of an elderly man in profile, powdered hair tied with a black ribbon, dark coat, white cravat, in a dim room lit from the left, holding two mercury thermometers with black bulbs upright side by side and studying them; one column stands visibly higher; a sliver of red spectrum on paper at lower left. Copperplate engraving, sepia cross-hatching, only the spectrum in colour. no text, no logos', draw: k4, refs: [ ref('p1', 'citation: the two standards kept in shade; "No. 3 never differed much from No. 2"'), ref('p2', 'citation: No. 3 kept as a fixed standard while Nos. 1 and 2 were exposed; positions swapped to confirm'), ref('crayon', 'likeness reference (three-quarter study of Herschel after Abbott)'), ref('tfh', 'rotoscope reference: hand holding a thermometer upright in close-up'), ], }, { n: 5, t: 54.0, title: 'Nothing to see', shot: 'ECU at paper level on the black bulb in the plain patch; the column runs up the frame; the standard in shade behind; last edge of red at the left margin', action: 'The mercury climbs in the thermometer that sits on paper where nothing is visible, while the standard in the shade behind it holds still. The paper stays plain.', mechanism: 'unseen rays → blackened ball (absorbs) → mercury (rises) ; instrument ⇝ viewer (the edge was the edge of the eye, not of the light) ; image ⊣ visualisation (refuses to show what is detected)', layers: [ { name: 'plain paper patch and shade (engraved)', source: 'INVENTED', ref: 0 }, { name: 'mercury column motion, rotoscoped and reversed', source: 'PHOTOGRAPHED', ref: 1 }, { name: 'thermometer No. 2 and standard No. 3', source: 'INVENTED', ref: 0 }, { name: 'last edge of red', source: 'PHOTOGRAPHED', ref: null }, ], prompt: 'Extreme close-up at paper level: a black-bulbed mercury thermometer on a small wooden ramp rests on plain ruled white paper in a dark room, its scale board rising steeply out of frame, mercury column high; behind it in shadow a second thermometer reads lower; only a faint red edge at the far left margin. Copperplate engraving, sepia hatching, no other colour, no glow. no text, no logos', draw: k5, refs: [ ref('p2', 'citation: the heating maximum lies "probably not less than half an inch beyond the last visible" red'), ref('research', 'rotoscope reference: close-up of a mercury column moving on a scale, run in reverse so it climbs'), ref('ipac', 'citation (popular account): temperature beyond the red was higher still'), ], }, ], }); })();