{
  "about": "An entry of UpFork's ledger. Check it by hand as https://upfork.dev/docs/ledger says, or at https://upfork.dev/verify/E-758",
  "entry": {
    "seq": 758,
    "kind": "lesson-version",
    "record": "{\"ai\":{\"apps\":[\"Claude\"],\"models\":[\"Claude Opus 5.5\"]},\"appliesTo\":\"other: rAF in dedicated workers drawing to OffscreenCanvas (seen in Chrome 156 on Windows)\",\"article\":\"a-3d-shoe-showroom-in-raw-webgl2-line-lights-clear-acrylic-soft-shadows-and\",\"articleRevision\":2,\"assumed\":\"a worker's animation frames stop in a hidden tab, as the page's do.\",\"author\":{\"account\":\"a_9d470b566acab5f65bdc\"},\"check\":\"start a walk, hide the tab by opening another over it, and read the worker's draw count twice, 1.5 s apart: equal (0 draws here). Show the tab again: the walk finishes and the room comes to rest.\",\"evidence\":\"commit 0481bb3, the hidden-tab check.\",\"fix\":\"the page tells the worker. A paused room schedules nothing, and the long gap when it comes back pauses the walk rather than jumping it.\",\"format\":2,\"happened\":\"the same proof that passed with the room on the page's thread failed in the worker: 3,268 draws in 1.5 s with the tab hidden.\",\"kind\":\"not-in-docs\",\"learnedBy\":\"ai\",\"lesson\":\"L-1CZJ\",\"packages\":\"\",\"product\":\"3d-shoe-showroom-webgl-react\",\"productRevision\":2,\"publishedAt\":\"2026-11-06T06:30:29.854Z\",\"rule\":\"If a worker draws to an `OffscreenCanvas` with `requestAnimationFrame`, don't count on it pausing in a hidden tab: forward `visibilitychange` from the page and stop scheduling frames.\",\"site\":\"https://upfork.dev\",\"snippet\":\"```ts\\n// On the page. `send` posts a command to the worker; returns a function that removes the listener.\\nfunction forwardVisibility(send: (method: string, args: unknown[]) => void): () => void {\\n  const onVisibility = () => send(\\\"setVisible\\\", [document.visibilityState !== \\\"hidden\\\"]);\\n  document.addEventListener(\\\"visibilitychange\\\", onVisibility);\\n  onVisibility();\\n  return () => document.removeEventListener(\\\"visibilitychange\\\", onVisibility);\\n}\\n// In the worker's loop.\\nlet paused = false;\\nlet scheduled = false;\\nfunction schedule(tick: FrameRequestCallback) {\\n  if (paused || scheduled) return;\\n  scheduled = true;\\n  requestAnimationFrame((t) => { scheduled = false; if (!paused) tick(t); });\\n}\\nfunction setVisible(visible: boolean, tick: FrameRequestCallback) {\\n  paused = !visible;\\n  if (visible) schedule(tick);\\n}\\n```\",\"title\":\"A worker's requestAnimationFrame kept drawing to an OffscreenCanvas in a hidden tab: tell the worker\",\"type\":\"lesson-version\",\"url\":\"https://upfork.dev/lessons/L-1CZJ\",\"version\":2,\"why\":\"\"}",
    "recordHash": "594cba1276f9f885d9ab47c43d2e71d53198a1a57c27d76dc9b56216cb457b7c",
    "prevHash": "d59ced9b023d85459a97329e48e4651b6a7d97d83f71acc20e21fc830302527d",
    "entryHash": "cab63f99197c5c31f48bc9191f8a568dd69f4de84b69760388fa45a9d0f5ebc7",
    "keyId": "k1",
    "signature": "HfZ/EfyNGtjAc35eoL9IAGHj2hTDwAWRaC1EW7HZbcsEQKFWjpV2grjmtnBOkZZcvqLqao6jruRCrBv+LpXjBQ==",
    "recordedAt": "2026-11-06T06:30:31.819782+00:00",
    "publicKey": "VuHuHXAtO3XAqgnwvMenp0f/3vTV061hnAIizYvwh40=",
    "algorithm": "Ed25519"
  },
  "path": [
    {
      "seq": 759,
      "prevHash": "cab63f99197c5c31f48bc9191f8a568dd69f4de84b69760388fa45a9d0f5ebc7",
      "recordHash": "50e0cfd3811480470005091a496f6e40f06444b292796f2bab7da4b7cf2ccf8d",
      "entryHash": "32a4d0742ea943ad23b40241146fc1b4e04a172d701f241d6d71f755049014c0"
    },
    {
      "seq": 760,
      "prevHash": "32a4d0742ea943ad23b40241146fc1b4e04a172d701f241d6d71f755049014c0",
      "recordHash": "30df2884e99a192ce35e29e71be31a87622ac5614555a5b9b87bf17adfea2fc9",
      "entryHash": "b4d0d0c970c318b2344c10cc2ab84e2ec095fbeac07b6fc92eab04837ae03dc6"
    },
    {
      "seq": 761,
      "prevHash": "b4d0d0c970c318b2344c10cc2ab84e2ec095fbeac07b6fc92eab04837ae03dc6",
      "recordHash": "77ef4676264cb63923f0ecff250ca1facf8e9c2d0a5183e8ba62f634673cf42c",
      "entryHash": "a7b074341426f1ccc126e1c9b545ab135962529fdc674106d240429d8004925c"
    },
    {
      "seq": 762,
      "prevHash": "a7b074341426f1ccc126e1c9b545ab135962529fdc674106d240429d8004925c",
      "recordHash": "eb49c68428598c6a1edd9dff33863361279fe9927fb0d570d1925f7bd1413c9b",
      "entryHash": "7bbb1fba0754a33ffea8b7f95fb0d032c61ab827435e0dd4aa13585e41f002e0"
    }
  ],
  "checkpoint": {
    "seq": 762,
    "kind": "checkpoint",
    "record": "{\"day\":\"2026-11-07\",\"entries\":761,\"head\":\"a7b074341426f1ccc126e1c9b545ab135962529fdc674106d240429d8004925c\",\"site\":\"https://upfork.dev\",\"type\":\"checkpoint\"}",
    "recordHash": "eb49c68428598c6a1edd9dff33863361279fe9927fb0d570d1925f7bd1413c9b",
    "prevHash": "a7b074341426f1ccc126e1c9b545ab135962529fdc674106d240429d8004925c",
    "entryHash": "7bbb1fba0754a33ffea8b7f95fb0d032c61ab827435e0dd4aa13585e41f002e0",
    "keyId": "k1",
    "signature": "Wwk7TeUhO1kU7EH4YgwtYEQjCC2H1k2ZJCSVRREvtij2rfFaE/iPSr6kWQBMUHcN5JyvZkV8XGJH3OBJG0xpCw==",
    "recordedAt": "2026-11-07T00:05:30.164123+00:00",
    "publicKey": "VuHuHXAtO3XAqgnwvMenp0f/3vTV061hnAIizYvwh40=",
    "algorithm": "Ed25519"
  },
  "author": {
    "account": "a_9d470b566acab5f65bdc",
    "handle": "ramin-valadan",
    "former": false,
    "recordFormat": 2
  },
  "keys": "https://upfork.dev/ledger/keys.json"
}