Spaces:
Running
Running
the model writes two bytes of the assembler; flicker strips; controls; one renderer
ce39eaa verified Download render.js from Unt1l1f1nd/fouriers-fire: direct link, hf CLI and curl.
- Browser
- Download file 3.32 kB
-
https://huggingface.co/spaces/Unt1l1f1nd/fouriers-fire/resolve/main/render.js
- Command line
-
hf download hf://spaces/Unt1l1f1nd/fouriers-fire/render.js
-
curl -L -o render.js https://huggingface.co/spaces/Unt1l1f1nd/fouriers-fire/resolve/main/render.js
3.32 kB
| // Shared drawing: the palette, painting a temperature field, colouring a word by its heat. | |
| // Used by index.html (the page) and record.html (the video), so they cannot drift apart. | |
| const PAL = new Uint8ClampedArray(256 * 4); | |
| { | |
| const stops = [[0, [0, 0, 0]], [.18, [70, 4, 0]], [.36, [190, 30, 0]], [.55, [245, 110, 10]], [.75, [255, 200, 60]], [.9, [255, 240, 180]], [1, [255, 255, 255]]]; | |
| for (let i = 0; i < 256; i++) { | |
| const t = i / 255; let a = stops[0], b = stops[1]; | |
| for (let s = 1; s < stops.length; s++) { if (t <= stops[s][0]) { a = stops[s - 1]; b = stops[s]; break; } } | |
| const f = (t - a[0]) / (b[0] - a[0] || 1); | |
| for (let c = 0; c < 3; c++) PAL[4 * i + c] = a[1][c] + (b[1][c] - a[1][c]) * f; | |
| PAL[4 * i + 3] = 255; | |
| } | |
| } | |
| function paintField(img, T, n) { | |
| const p = img.data; | |
| for (let i = 0; i < n; i++) { const c = 4 * Math.min(255, T[i] * 255 | 0); p[4 * i] = PAL[c]; p[4 * i + 1] = PAL[c + 1]; p[4 * i + 2] = PAL[c + 2]; p[4 * i + 3] = 255; } | |
| } | |
| // a word's heat on the bottom row, from the sentence score s and the word's share | |
| function wordHeat(s, share) { return Math.max(.15, Math.min(1, .6 + .25 * s + 1.8 * share)); } | |
| function wordColor(h, share) { return share < -.05 ? '#4E8FCB' : `rgb(${Math.max(125, Math.round(255 * h))},${Math.round(110 + 120 * h * h)},${Math.round(92 + 40 * h)})`; } | |
| // the per-column ember heat for a fluid of width W | |
| function emberHeat(W, words, shares, s) { | |
| const heat = new Float32Array(W), x0 = W * .12, x1 = W * .88, span = (x1 - x0) / words.length; | |
| words.forEach((w, i) => { const h = wordHeat(s, shares[i]); for (let x = Math.round(x0 + i * span); x < Math.round(x0 + (i + 1) * span) - 1; x++) heat[x] = h; }); | |
| return heat; | |
| } | |
| // the 1990s trick, as on the long page: Fourier with a constant updraft. Used here only for its brightness. | |
| function makeTrick(W, H) { | |
| const h = new Float32Array(W * (H + 4)), ember = new Float32Array(W), K = { D: .3, k: .012, p: .5 }; let mean = 0; | |
| function step() { | |
| for (let x = 0; x < W; x++) { if (Math.random() < .08) ember[x] = Math.random() < K.p ? 1 : 0; for (let y = H; y < H + 4; y++) h[y * W + x] = ember[x]; } | |
| let sum = 0; | |
| for (let y = 0; y < H; y++) { const r = (y + 1) * W; for (let x = 0; x < W; x++) { | |
| const xc = Math.min(W - 1, Math.max(0, x + (Math.random() * 3 | 0) - 1)), xl = xc ? xc - 1 : 0, xr = xc < W - 1 ? xc + 1 : xc; | |
| const s = (1 - K.D) * h[r + xc] + K.D * .5 * (h[r + xl] + h[r + xr]) - K.k * (.5 + Math.random()); const t = s > 0 ? s : 0; h[y * W + x] = t; sum += t; } } | |
| mean = sum / (W * H); | |
| } | |
| return { step, K, mean: () => mean }; | |
| } | |
| // a sparkline of the last N samples, drawn into a small canvas | |
| function sparkline(canvas, buf, n, i, color) { | |
| const g = canvas.getContext('2d'), W = canvas.width, H = canvas.height; | |
| g.clearRect(0, 0, W, H); | |
| if (n < 2) return; | |
| let lo = Infinity, hi = -Infinity; for (let j = 0; j < n; j++) { const v = buf[(i - n + j + buf.length) % buf.length]; if (v < lo) lo = v; if (v > hi) hi = v; } | |
| const span = hi - lo || 1e-6; | |
| g.strokeStyle = color; g.lineWidth = 1.5; g.beginPath(); | |
| for (let j = 0; j < n; j++) { const v = buf[(i - n + j + buf.length) % buf.length]; const x = j / (buf.length - 1) * W, y = H - 2 - (v - lo) / span * (H - 4); j ? g.lineTo(x, y) : g.moveTo(x, y); } | |
| g.stroke(); | |
| } | |