{
  "schema_version": 1,
  "scope": "Symmetry / symmetry-draft",
  "chapter": "chapter.md",
  "chapter_source": "content/drafts/symmetry-draft.md",
  "portable_chapter": "chapter-portable.md",
  "chapter_media_map": "chapter-media.md",
  "chapter_assets": [
    {
      "path": "content/drafts/animations/symmetry-pool-table-poster.png",
      "original_reference": "animations/symmetry-pool-table-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-pool-table-poster.png",
      "sha256": "00096dae4ffa8a9942f6430d5b1b17fe4087fc34221fac5dd1244c6036457059",
      "bytes": 125050,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-pool-table-poster.png"
    },
    {
      "path": "content/drafts/diagrams/symmetry-triangle.svg",
      "original_reference": "diagrams/symmetry-triangle.svg",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-triangle.svg",
      "sha256": "040ff4c5d82c921137c8600e2230b24785d18d82d2e05f8021c62491e432b305",
      "bytes": 332,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-triangle.svg"
    },
    {
      "path": "content/drafts/animations/symmetry-triangle-actions-poster.png",
      "original_reference": "animations/symmetry-triangle-actions-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-triangle-actions-poster.png",
      "sha256": "3e6f6f4d4aff4982d2eec8da09caca895f16a2b1181e9ba7a444cce4bee82433",
      "bytes": 8014,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-triangle-actions-poster.png"
    },
    {
      "path": "content/drafts/diagrams/symmetry-d3-carriers.svg",
      "original_reference": "diagrams/symmetry-d3-carriers.svg",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-d3-carriers.svg",
      "sha256": "a0f3df5f82835b83f891dbadf00ca87e346531df0e16401cd9586c2a527b4755",
      "bytes": 1192,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-d3-carriers.svg"
    },
    {
      "path": "content/drafts/diagrams/symmetry-d3-vertices-to-vector.svg",
      "original_reference": "diagrams/symmetry-d3-vertices-to-vector.svg",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-d3-vertices-to-vector.svg",
      "sha256": "8a539b614b605f56a6ff1778ede62326b3ce57c6527ed0c74658f834ea1048af",
      "bytes": 1307,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-d3-vertices-to-vector.svg"
    },
    {
      "path": "content/drafts/animations/symmetry-d3-rotations-vs-flips-poster.png",
      "original_reference": "animations/symmetry-d3-rotations-vs-flips-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-d3-rotations-vs-flips-poster.png",
      "sha256": "d16da5b4993ab34996a3e598671f7a22ed28aea79f2bdac654adfc7cad504888",
      "bytes": 49475,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-d3-rotations-vs-flips-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-d3-irrep-collapse-poster.png",
      "original_reference": "animations/symmetry-d3-irrep-collapse-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-d3-irrep-collapse-poster.png",
      "sha256": "498df4eb905d2b0f50cd7d37f4235e4fe66c3227115ea6157a5f3a01f82292ca",
      "bytes": 41497,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-d3-irrep-collapse-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-continuous-so2-translation-contact-sheet.png",
      "original_reference": "animations/symmetry-continuous-so2-translation-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-continuous-so2-translation-contact-sheet.png",
      "sha256": "edd8e0ee4e9d0e21f1f071623dbf604cdc1b347527a94a4c25f35cec13d2b511",
      "bytes": 77708,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-continuous-so2-translation-contact-sheet.png"
    },
    {
      "path": "content/drafts/diagrams/tangent-plane-curved-surface.png",
      "original_reference": "diagrams/tangent-plane-curved-surface.png",
      "file": "chapter-assets/content/drafts/diagrams/tangent-plane-curved-surface.png",
      "sha256": "0150241766d77f49db77e9113ffb5c928c7eb91e5673fbf6fc306afd0a8c7873",
      "bytes": 131862,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/tangent-plane-curved-surface.png"
    },
    {
      "path": "content/drafts/diagrams/so2-tangent-at-identity.png",
      "original_reference": "diagrams/so2-tangent-at-identity.png",
      "file": "chapter-assets/content/drafts/diagrams/so2-tangent-at-identity.png",
      "sha256": "57eb0d00f74cb05e1c19fd107768cea2ef58fd395607d5c4df74578c634c2917",
      "bytes": 43094,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/so2-tangent-at-identity.png"
    },
    {
      "path": "content/drafts/animations/symmetry-so2-vector-field-flow-contact-sheet.png",
      "original_reference": "animations/symmetry-so2-vector-field-flow-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-so2-vector-field-flow-contact-sheet.png",
      "sha256": "18d0cd1ad5ad47cd729df78d1880394ac2e90175c6ced945b2d554c6a349b8c7",
      "bytes": 127250,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-so2-vector-field-flow-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-rotation-vector-invariants-contact-sheet.png",
      "original_reference": "animations/symmetry-rotation-vector-invariants-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-rotation-vector-invariants-contact-sheet.png",
      "sha256": "6aaa41642ada40d3f63e29e4a72df4c9d639915724281972e0c73668892e9a31",
      "bytes": 69178,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-rotation-vector-invariants-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-hyperbolic-rotation-contact-sheet.png",
      "original_reference": "animations/symmetry-hyperbolic-rotation-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-hyperbolic-rotation-contact-sheet.png",
      "sha256": "7a24fed412e2b9244c04b57f3264045d5f4b99e4e89450b797c7b4af5d8bddf0",
      "bytes": 85170,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-hyperbolic-rotation-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-so3-rotation-order-contact-sheet.png",
      "original_reference": "animations/symmetry-so3-rotation-order-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-so3-rotation-order-contact-sheet.png",
      "sha256": "755f02c3c4a03cba63c3092fd5f36a96cdcdd31f3b3777265cc25e5a25016202",
      "bytes": 132960,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-so3-rotation-order-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-translation-point-linearity-failure-contact-sheet.png",
      "original_reference": "animations/symmetry-translation-point-linearity-failure-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-translation-point-linearity-failure-contact-sheet.png",
      "sha256": "fe63c51519d4ed3b591a281176e4a6579ea51811834b295cda23706d23a60931",
      "bytes": 45442,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-point-linearity-failure-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-translation-function-linearity-contact-sheet.png",
      "original_reference": "animations/symmetry-translation-function-linearity-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-translation-function-linearity-contact-sheet.png",
      "sha256": "2b84696811059d0733c65829d3d55d42fead7a40b3a6783507dfd878382338c5",
      "bytes": 72565,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-function-linearity-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-function-translation-shape-poster.png",
      "original_reference": "animations/symmetry-function-translation-shape-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-function-translation-shape-poster.png",
      "sha256": "d0841ac8de68e675dfb1714fabb3c51a212b1c132d4b12eeb0fdd1c72738e887",
      "bytes": 85122,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-function-translation-shape-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-translation-tangent-zoom-contact-sheet.png",
      "original_reference": "animations/symmetry-translation-tangent-zoom-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-translation-tangent-zoom-contact-sheet.png",
      "sha256": "6045fca13932154fc17f3a787c25c06c19aefcc1bf2ad8255b355da23ceb4839",
      "bytes": 53942,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-tangent-zoom-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-eigenbasis-stretch-contact-sheet.png",
      "original_reference": "../../content/drafts/animations/symmetry-eigenbasis-stretch-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-eigenbasis-stretch-contact-sheet.png",
      "sha256": "08ef27bdd424e4686078395ce16f564adff7af5d16e006d9680208da330a02f4",
      "bytes": 59171,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-eigenbasis-stretch-contact-sheet.png"
    },
    {
      "path": "content/drafts/diagrams/symmetry-complex-plane-vector.png",
      "original_reference": "diagrams/symmetry-complex-plane-vector.png",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-complex-plane-vector.png",
      "sha256": "eff2c27c2b8b31c5cb7ce3f4b970feac86d56624cfa4515b8255fce627361844",
      "bytes": 48009,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-complex-plane-vector.png"
    },
    {
      "path": "content/drafts/diagrams/symmetry-complex-unit-tangent.png",
      "original_reference": "diagrams/symmetry-complex-unit-tangent.png",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-complex-unit-tangent.png",
      "sha256": "2916ed52380055f55df0885e96e41817fd79a78b2edcbf35f6a496222a87494b",
      "bytes": 49087,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-complex-unit-tangent.png"
    },
    {
      "path": "content/drafts/animations/symmetry-complex-plane-wave-v2-poster.png",
      "original_reference": "animations/symmetry-complex-plane-wave-v2-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-complex-plane-wave-v2-poster.png",
      "sha256": "c4176af5315f6b8544ad98dfbcb9b05fcc18dcdef25b77fa0c31428a5eb5556f",
      "bytes": 153935,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-complex-plane-wave-v2-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-fourier-three-tone-packet-contact-sheet.png",
      "original_reference": "animations/symmetry-fourier-three-tone-packet-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-fourier-three-tone-packet-contact-sheet.png",
      "sha256": "071f37075bdeac1f668be410bf0c059ca443ba5e96a1b3fe5109ecdb2070284b",
      "bytes": 243863,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-fourier-three-tone-packet-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-ccr-x-k-translations-symmetric-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-x-k-translations-symmetric-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-ccr-x-k-translations-symmetric-poster.png",
      "sha256": "1eae94119f091ada833334e999ffacae51ecd91afc522fcc4a3a23f86bbc0b40",
      "bytes": 98072,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-x-k-translations-symmetric-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-ccr-unitarity-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-unitarity-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-ccr-unitarity-poster.png",
      "sha256": "399b903eb1c3be826e86f2769f368f1f5e6d2a73d030bbdcd45b1d4b444b8c95",
      "bytes": 181135,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-unitarity-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-complex-phase-modes-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-complex-phase-modes-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-complex-phase-modes-poster.png",
      "sha256": "31e6132f4068fea34796c903ab057bc847b336799de8a12e171b03c3f6f1bff5",
      "bytes": 268970,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-complex-phase-modes-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-ccr-loop-complex-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-loop-complex-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-ccr-loop-complex-poster.png",
      "sha256": "e1999f38a6c459d0f3a26e7c60143b701792174c6e5a69f7fe8fc030bb0c352d",
      "bytes": 190869,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-loop-complex-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-xk-fourier-complex-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-xk-fourier-complex-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-xk-fourier-complex-poster.png",
      "sha256": "f13216b2fdc90a6a6065090ac8300bc91376ee153e07fce9c580289e6190f37a",
      "bytes": 161422,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-xk-fourier-complex-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-xk-squared-magnitudes-contact-sheet.png",
      "original_reference": "../../content/drafts/animations/symmetry-xk-squared-magnitudes-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-xk-squared-magnitudes-contact-sheet.png",
      "sha256": "d439da888b0caaa586405be8fd58a7338e38320997e1fb106428907233f58ef6",
      "bytes": 65559,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-xk-squared-magnitudes-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-short-wave-beams-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-short-wave-beams-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-short-wave-beams-poster.png",
      "sha256": "6f06b9538a6f103acc5f52089b9cba9b8456fa072aa2af5cfaef7e15c53589b8",
      "bytes": 79940,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-short-wave-beams-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-poster.png",
      "sha256": "fa36a8a459810135a685c844b5b4bd2d31d9512508ca542af77d11b67574c643",
      "bytes": 952133,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-poster.png"
    },
    {
      "path": "content/drafts/diagrams/symmetry-double-slit-two-path-phasor-sum-shortwave.png",
      "original_reference": "../../content/drafts/diagrams/symmetry-double-slit-two-path-phasor-sum-shortwave.png",
      "file": "chapter-assets/content/drafts/diagrams/symmetry-double-slit-two-path-phasor-sum-shortwave.png",
      "sha256": "ffd9c6edebcc2b2de352c7ab7a01b7a36fb091546c022a7df6d1c327cbbe6c49",
      "bytes": 104717,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/diagrams/symmetry-double-slit-two-path-phasor-sum-shortwave.png"
    },
    {
      "path": "content/drafts/animations/symmetry-many-slit-paths-phasors-interference-contact-sheet.png",
      "original_reference": "../../content/drafts/animations/symmetry-many-slit-paths-phasors-interference-contact-sheet.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-many-slit-paths-phasors-interference-contact-sheet.png",
      "sha256": "e12be2a77bdd42fab8c9cfd9dd208a6cb6b8a4c8b1bd012a6007fd105fd9b0df",
      "bytes": 1042841,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-many-slit-paths-phasors-interference-contact-sheet.png"
    },
    {
      "path": "content/drafts/animations/symmetry-schematic-screens-v3-concise-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-schematic-screens-v3-concise-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-schematic-screens-v3-concise-poster.png",
      "sha256": "2c1a3a17c51151b65274eac29ecb60aaa4c4f3f32d372b471c9e7e2f1654bf7f",
      "bytes": 487957,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-schematic-screens-v3-concise-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-phase-remainder-spinners-run-3.png",
      "original_reference": "../../content/drafts/animations/symmetry-phase-remainder-spinners-run-3.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-phase-remainder-spinners-run-3.png",
      "sha256": "673b2252dddecb22163dbd5aef9f45be81bf52058fafd63ed07a4c74747b488f",
      "bytes": 69006,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-phase-remainder-spinners-run-3.png"
    },
    {
      "path": "content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan-lambda-3.png",
      "original_reference": "../../content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan-lambda-3.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan-lambda-3.png",
      "sha256": "dfdd9c849aefac98417cb0a07a448e409f2b9e1b783ba37384794b061207ccff",
      "bytes": 138490,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-spectrum-path-diamond-wavelength-scan-lambda-3.png"
    },
    {
      "path": "content/drafts/animations/symmetry-amplitude-probability-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-amplitude-probability-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-amplitude-probability-poster.png",
      "sha256": "7235d3c0594d842bb303d3315f3d9e77d5dd902aae14d876dc8a96aecbf5d937",
      "bytes": 223462,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-amplitude-probability-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-collision-shared-scale-poster.png",
      "original_reference": "animations/symmetry-collision-shared-scale-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-collision-shared-scale-poster.png",
      "sha256": "867215d4d3ee00cf28d36418f1d36a7e67b079620cd2adad2434600332d2d88e",
      "bytes": 102626,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-collision-shared-scale-poster.png"
    },
    {
      "path": "content/drafts/animations/symmetry-action-phase-desktop-poster.png",
      "original_reference": "../../content/drafts/animations/symmetry-action-phase-desktop-poster.png",
      "file": "chapter-assets/content/drafts/animations/symmetry-action-phase-desktop-poster.png",
      "sha256": "98380c011c5599248a861e865909c8ff120be357bf49d411984aca02115c9919",
      "bytes": 77912,
      "public_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-action-phase-desktop-poster.png"
    }
  ],
  "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/",
  "chapter_sha256": "332b4f8496e3e2864d06ea9ab7484a6aa492bbc7c9b727e0dbff70f65918c972",
  "git_head": "974f9de352542963116af37c417ad68cd3a2c101",
  "frame_limitations": "These are sparse samples decoded from the current MP4, not newly rendered illustrations. They can support checks of the sampled states and labels, but cannot establish continuous motion, timing, transitions, or the absence of problems between samples. Use the full MP4 when judging those properties.",
  "source_provenance_limitations": "Source SHA-256 values identify the exact downloadable bytes in this packet. The source mapping and recorded checks explain the likely generation pipeline; they do not prove that these exact source bytes produced the movie. A GitHub link pinned to a commit is provided only when the delivered source bytes exactly match that path at the build's Git HEAD.",
  "generator_check_limitations": "These are existing author-produced generator reports, copied without changes. Their checks were not rerun for this packet and are not independent certification. A report may describe an earlier generation run; inspect its contents before applying its claims to the current movie.",
  "animations": [
    {
      "video": "content/drafts/animations/symmetry-pool-table.mp4",
      "original_reference": "animations/symmetry-pool-table.mp4",
      "basename": "symmetry-pool-table.mp4",
      "slug": "symmetry-pool-table",
      "chapter_section": "Symmetry",
      "caption": "Symmetry transforms don't change how pool balls behave",
      "image_description": "The same pool-ball collision under position and time translations, rotation, and a velocity boost",
      "video_link_line": 6,
      "context_before": [
        {
          "start_line": 2,
          "end_line": 2,
          "markdown": "Strike a pool ball with a cue, and the balls move in an expected way. Move the table over a few feet, and the balls move in recognizably the same way. Wait a few minutes, and the balls move in the same way. Turn the pool table a few degrees, and the balls still move the same way. Put the pool table on a train at constant velocity, and, again, the balls move in the same way. These are the manifest \"symmetries\" of the world we live in -- position and time translation, rotation, and velocity \"boosts.\""
        }
      ],
      "context_after": [
        {
          "start_line": 10,
          "end_line": 10,
          "markdown": "Our pool game illustrates what we mean by symmetries of physical behavior, but why should we start our story here? We will argue that symmetry constrains both the laws that govern physical evolution and the classification of the objects that undergo that evolution. The term \"symmetry\" in this context may not at first glance seem like the same concept as, say, a triangle's symmetry, but it precisely is, as we will see."
        },
        {
          "start_line": 12,
          "end_line": 12,
          "markdown": "We will start with the familiar world of these discrete symmetries and build up the definitions we need from there. Then we will turn to “continuous symmetries,” specifically rotations and translations, where we will encounter a new branch of math, Lie algebra, that joins symmetry ideas with calculus. We will discover that waves arise naturally when we represent translational symmetry, and this will lead us to a discussion of Fourier analysis. Finally, we will situate these ideas in the context of quantum mechanics."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_pool_table.py"
      ],
      "generation_note": "Main script --render draws the equal-mass elastic-disk trials and encodes frames directly with FFmpeg. --check and --encoded-check produce the companion numerical and decoded-video reports. It has no local Python imports or input movie.",
      "evidence": "The script's NAME and output match the linked movie. Matching validation and encoded-validation JSON record 1600 x 960, 30 fps, 420 frames, 14 s. The reports contain no movie hash, so this is source/output-name attribution rather than a hash-attested build.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_pool_table.py",
          "file": "sources/scripts/generate_symmetry_pool_table.py.txt",
          "sha256": "b16887b92be1a8b23c5932ce457c6c1f7db0ca90519d0b963b0a377448a37c7f",
          "bytes": 16245,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_pool_table.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-pool-table-validation.json",
          "file": "symmetry-pool-table/checks/symmetry-pool-table-validation.json",
          "sha256": "0563fa0c6d81a153869ef1d1fec1518d4fcdf84e15cafa840428443ffaccde0c",
          "bytes": 763
        },
        {
          "path": "content/drafts/animations/symmetry-pool-table-encoded-validation.json",
          "file": "symmetry-pool-table/checks/symmetry-pool-table-encoded-validation.json",
          "sha256": "dc55bdc42a1d12e38e8008a0a667e2a668175b2a06e7e871cb026166f1712a6e",
          "bytes": 142
        }
      ],
      "manifest": "symmetry-pool-table/manifest.json",
      "manifest_sha256": "4e49d8902a3d76812e66e669e6de06c59e81e89bbf443ac95a50023b1f4a03d2",
      "frame_directory": "symmetry-pool-table/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-pool-table.mp4",
        "video_sha256": "60ac9f04ccefa11525471e6dc5bcced6497776b8f51b767f1c00ffbddfbbb4a6",
        "duration_seconds": 14.0,
        "fps": 30.0,
        "frame_count": 420,
        "width": 1600,
        "height": 960,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "daade1c411a5d269f95bf6b4768b8d926750241d7b51dcb336187184c707b5e4"
          },
          {
            "file": "frame-02.jpg",
            "index": 38,
            "time_seconds": 1.266667,
            "sha256": "acaea3d1b4980aa5b609f385251d899d131c92b64bd3889743e7c5f36a974c94"
          },
          {
            "file": "frame-03.jpg",
            "index": 76,
            "time_seconds": 2.533333,
            "sha256": "36cce8fe53c605d404dbac55630c41e0288c1c926467d0fc23470511607f042b"
          },
          {
            "file": "frame-04.jpg",
            "index": 114,
            "time_seconds": 3.8,
            "sha256": "17dd56a3f7f4c1096bd60adc602756685110c379544438fdc405f6bac49eb895"
          },
          {
            "file": "frame-05.jpg",
            "index": 152,
            "time_seconds": 5.066667,
            "sha256": "66cc354c4306deee4aa72c4890bbbc6e45bc06ed0b7f7699f798fc477742c416"
          },
          {
            "file": "frame-06.jpg",
            "index": 190,
            "time_seconds": 6.333333,
            "sha256": "29b589f8fc34ec412479fe45061aa66ffc1de1f936ce78a5f23604ffb5aef6a7"
          },
          {
            "file": "frame-07.jpg",
            "index": 229,
            "time_seconds": 7.633333,
            "sha256": "589f015a378ef53c4bc10999a95a58dda4e8cf50a2ba5b681ef9cb91cb4646e7"
          },
          {
            "file": "frame-08.jpg",
            "index": 267,
            "time_seconds": 8.9,
            "sha256": "0c2691b6c2c3ab9501a76334e57d187feef8b59d30191bfdf7fecd3821b684d0"
          },
          {
            "file": "frame-09.jpg",
            "index": 305,
            "time_seconds": 10.166667,
            "sha256": "560ef28640435c03b062ee17290ac9a30453bff25e36a795fd6b141ddd6992c2"
          },
          {
            "file": "frame-10.jpg",
            "index": 343,
            "time_seconds": 11.433333,
            "sha256": "ee7339fa8d57db279b0575566989cdb0d86a2ba812eab1fa6d426c508aea9a9c"
          },
          {
            "file": "frame-11.jpg",
            "index": 381,
            "time_seconds": 12.7,
            "sha256": "cc4aa4b36fc4d121877da364335e8c3cc978220dd75c40dcade12cf5ed0d4d2e"
          },
          {
            "file": "frame-12.jpg",
            "index": 419,
            "time_seconds": 13.966667,
            "sha256": "de2b6edd7c31efd5cdc366b386933b336e66cf7acb0bd503585fa32a3e5bbe64"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "7eadf769f15e8620cca4f3bdf1ba27ce08cc44437e2a3881462daf6a9ddb4a10"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-pool-table",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-pool-table",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-pool-table.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-pool-table/"
    },
    {
      "video": "content/drafts/animations/symmetry-triangle-actions.mp4",
      "original_reference": "animations/symmetry-triangle-actions.mp4",
      "basename": "symmetry-triangle-actions.mp4",
      "slug": "symmetry-triangle-actions",
      "chapter_section": "Discrete symmetries",
      "caption": "A triangle's symmetries",
      "image_description": "Triangle symmetry actions",
      "video_link_line": 50,
      "context_before": [
        {
          "start_line": 40,
          "end_line": 46,
          "markdown": "We can see its obvious symmetry. To categorize its symmetry we can write down all the actions that leave it unchanged:\n1. Do nothing\n2. Rotate 120°\n3. Rotate 240°\n4. Flip along an axis\n5. Flip then rotate 120°\n6. Flip then rotate 240°"
        }
      ],
      "context_after": [
        {
          "start_line": 54,
          "end_line": 54,
          "markdown": "We say the triangle \"belongs to the $D_3$ symmetry **group**.\" Any number of objects possess $D_3$ symmetry."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_triangle_actions_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the triangle-action frames with Pillow and encodes them with FFmpeg, then writes a poster. It has no local Python imports or input movie.",
      "evidence": "main() explicitly writes symmetry-triangle-actions.mp4; constants specify 900 x 620 at 18 fps. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_triangle_actions_animation.py",
          "file": "sources/scripts/generate_symmetry_triangle_actions_animation.py.txt",
          "sha256": "233476265d3bbde8b9736f509059e0346cc778c45152c69fde68880ab1bb89c5",
          "bytes": 6877,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_triangle_actions_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-triangle-actions/manifest.json",
      "manifest_sha256": "06f221ffd63be870dc5c3eaeb336ed350285b26804f27c56520bc81edefbbfb0",
      "frame_directory": "symmetry-triangle-actions/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-triangle-actions.mp4",
        "video_sha256": "d03afb8e8f4d486b40907a7d551496836714e27650b67037f92d42f2a7ee44a9",
        "duration_seconds": 20.0,
        "fps": 18.0,
        "frame_count": 360,
        "width": 900,
        "height": 620,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "6b04b835e219e211f5c4947b8d654ae75dadc0b771fff4ce693a9b39b696b302"
          },
          {
            "file": "frame-02.jpg",
            "index": 33,
            "time_seconds": 1.833333,
            "sha256": "bebdcf9801816b1c21b01f7183c1bc69b2fdf6f0cb95d358c0a7f0f6d1d4c1d2"
          },
          {
            "file": "frame-03.jpg",
            "index": 65,
            "time_seconds": 3.611111,
            "sha256": "ba1b7ce117e99ae6a297dcd90d05890827d84466a5c2768370d6116001c9c872"
          },
          {
            "file": "frame-04.jpg",
            "index": 98,
            "time_seconds": 5.444444,
            "sha256": "22f3b9092fd340993c8861043f27f4121d901ea640c6a915f45b4ee863251813"
          },
          {
            "file": "frame-05.jpg",
            "index": 131,
            "time_seconds": 7.277778,
            "sha256": "88d3da9dce5a18f0fb3973c445fea60ee081e8a7e97971a126e1ee5baebb620c"
          },
          {
            "file": "frame-06.jpg",
            "index": 163,
            "time_seconds": 9.055556,
            "sha256": "10dd6adf08e37a8a959eab183d5f7fe97a3016f466356259f3cb53f6d7d783ad"
          },
          {
            "file": "frame-07.jpg",
            "index": 196,
            "time_seconds": 10.888889,
            "sha256": "46f05ac046c4ef476c35aac08ba730106f9d3950a055dbbffe14179bd2431b1e"
          },
          {
            "file": "frame-08.jpg",
            "index": 228,
            "time_seconds": 12.666667,
            "sha256": "db0817ad3a6c99c30217c5b79dd12b088be6921b2c7e38d7c55d456c4c97cf59"
          },
          {
            "file": "frame-09.jpg",
            "index": 261,
            "time_seconds": 14.5,
            "sha256": "18f932587050ee4d039f2c551d105454e78ca93a77cfaf2ab57f39d1c74d1f0c"
          },
          {
            "file": "frame-10.jpg",
            "index": 294,
            "time_seconds": 16.333333,
            "sha256": "694b32f59436913ebd1905c9771b7083e8c4823c7e0f285bfc15b7db19b7ea6c"
          },
          {
            "file": "frame-11.jpg",
            "index": 326,
            "time_seconds": 18.111111,
            "sha256": "8a017ff84b541677aaf5b9fc2b796bdbf55d3b59eff29453bb3f7c5a69c6960c"
          },
          {
            "file": "frame-12.jpg",
            "index": 359,
            "time_seconds": 19.944444,
            "sha256": "f698da08fa1cc036f5f815106b09ff43dcfc27c46cba0f97e407d6ad400c3d1b"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "6ae6dfb067d7133036c00462e9005c257f9893a94219e2d4c97966c046b3aba5"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-triangle-actions",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-triangle-actions",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-triangle-actions.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-triangle-actions/"
    },
    {
      "video": "content/drafts/animations/symmetry-d3-rotations-vs-flips.mp4",
      "original_reference": "animations/symmetry-d3-rotations-vs-flips.mp4",
      "basename": "symmetry-d3-rotations-vs-flips.mp4",
      "slug": "symmetry-d3-rotations-vs-flips",
      "chapter_section": "Representations",
      "caption": "$D_3$ in 3-dimensional representation",
      "image_description": "D3 rotations and flips with labeled component axes and a fixed equal-components axis",
      "video_link_line": 124,
      "context_before": [
        {
          "start_line": 118,
          "end_line": 118,
          "markdown": "The matrices that provide a set of transformations from which all other operations can be constructed are the symmetry's **generators**. For example, in the 3-dimensional representation of $D_3$, all operations can be constructed from one rotation matrix and 3 flip matrices, one for each axis. "
        },
        {
          "start_line": 120,
          "end_line": 120,
          "markdown": "Visually, permutations that correspond to rotations of the triangle are 120-degree rotations about a diagonal axis in this vector space, while those that correspond to flips are flips about planes that contain the diagonal axis."
        }
      ],
      "context_after": [
        {
          "start_line": 128,
          "end_line": 128,
          "markdown": "Now, we can notice something about these visualizations. The transformation of any vector lies in a plane, and all such planes are parallel to one another. Thus, if we subtract the average of a vector's components from each component, that is, if we move the point where the plane intersects the axis of rotation to the origin, we preserve the permutation structure of the transformations. We thus see that the $D_3$ symmetry is just as well represented as 120-degree rotations in the subspace of a 2-dimensional plane."
        },
        {
          "start_line": 130,
          "end_line": 130,
          "markdown": "For our earlier vector, the average of the components is 3. We can separate it into a common part and a zero-sum part."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_d3_rotations_vs_flips_animation.py",
        "scripts/symmetry_d3_rendering.py"
      ],
      "generation_note": "Run the main script without arguments. It uses symmetry_d3_rendering for shared geometry, transformations, and drawing, then encodes its frames with FFmpeg. The imported module is a code dependency, not an input movie.",
      "evidence": "The generator explicitly writes symmetry-d3-rotations-vs-flips.mp4; constants specify 1200 x 700 at 24 fps. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_d3_rotations_vs_flips_animation.py",
          "file": "sources/scripts/generate_symmetry_d3_rotations_vs_flips_animation.py.txt",
          "sha256": "c2214d1c0f6c2cfbac1a752ac8d4a377f57275b53cae14c15c3fbd20a3829e01",
          "bytes": 5196,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_d3_rotations_vs_flips_animation.py"
        },
        {
          "path": "scripts/symmetry_d3_rendering.py",
          "file": "sources/scripts/symmetry_d3_rendering.py.txt",
          "sha256": "4be9f7169cfe3654b9a1457de70a65e29dd20cc4863c7445a5ca6155f4453d6c",
          "bytes": 13034,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/symmetry_d3_rendering.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-d3-rotations-vs-flips/manifest.json",
      "manifest_sha256": "21401ad59a3cf6ae533e6c73b46a61af0f0e5c606dbf13963361be7d02ccf2d5",
      "frame_directory": "symmetry-d3-rotations-vs-flips/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-d3-rotations-vs-flips.mp4",
        "video_sha256": "7aeb470b58e5a3bbc7801863594e883b9f1beb28283a29554a6a170abc513dcb",
        "duration_seconds": 6.5,
        "fps": 24.0,
        "frame_count": 156,
        "width": 1200,
        "height": 700,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "52b06a57fddc8085f013546f00ca59d368f7286d10855b7429b0ad8529468c85"
          },
          {
            "file": "frame-02.jpg",
            "index": 14,
            "time_seconds": 0.583333,
            "sha256": "75d6506795e024a2b13a47ec8718a7e6e188c06dcafcb2d47e5002b6c7e068ca"
          },
          {
            "file": "frame-03.jpg",
            "index": 28,
            "time_seconds": 1.166667,
            "sha256": "b4c1f6320acc7bc418f35690df9368edf23996d44296020e463768d5b707a244"
          },
          {
            "file": "frame-04.jpg",
            "index": 42,
            "time_seconds": 1.75,
            "sha256": "a0acfd2c7b671534f2735395ef963335c614a3dd3599b4a2570b8c302570f09c"
          },
          {
            "file": "frame-05.jpg",
            "index": 56,
            "time_seconds": 2.333333,
            "sha256": "29e62d714e96334cd418d1102b644827cac1e7835549c2637cc696ea1e4a857b"
          },
          {
            "file": "frame-06.jpg",
            "index": 70,
            "time_seconds": 2.916667,
            "sha256": "75be4c0ea31ef32615594869f9534f3e0b7dc8f042f11a82aef390dcfc5b1bfc"
          },
          {
            "file": "frame-07.jpg",
            "index": 85,
            "time_seconds": 3.541667,
            "sha256": "1a63199342c1cfbfeca659ba339f1245e3c54d57f3a0a018fd8f76b3117ecf71"
          },
          {
            "file": "frame-08.jpg",
            "index": 99,
            "time_seconds": 4.125,
            "sha256": "04adff850d6409773f4fe58a0f33d44675820e473cd2a8639553f5763cb768b1"
          },
          {
            "file": "frame-09.jpg",
            "index": 113,
            "time_seconds": 4.708333,
            "sha256": "5752439b37ad9d339fcb811789c20600459179bb31a7e2197cf9eb0eba1940ef"
          },
          {
            "file": "frame-10.jpg",
            "index": 127,
            "time_seconds": 5.291667,
            "sha256": "58a7fa50b29717874d00b7ca0377b4a252a73c860be818e3ec0fe4b66d966a19"
          },
          {
            "file": "frame-11.jpg",
            "index": 141,
            "time_seconds": 5.875,
            "sha256": "913fe367e782e463236873804f7cab674e7b6932d87fee1cb39a416b26177981"
          },
          {
            "file": "frame-12.jpg",
            "index": 155,
            "time_seconds": 6.458333,
            "sha256": "49a28ec8664ee77bf0b02beabdd15d249859a5aac09458463d1f67164472b02f"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "216e7fdf05aff44edaf6a44d7008ff39e01b73203ccadd4d0e0859a47ffaa082"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-d3-rotations-vs-flips",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-d3-rotations-vs-flips",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-d3-rotations-vs-flips.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-d3-rotations-vs-flips/"
    },
    {
      "video": "content/drafts/animations/symmetry-d3-irrep-collapse.mp4",
      "original_reference": "animations/symmetry-d3-irrep-collapse.mp4",
      "basename": "symmetry-d3-irrep-collapse.mp4",
      "slug": "symmetry-d3-irrep-collapse",
      "chapter_section": "Representations",
      "caption": "Reducing 3-dimensional representation to 2-dimensional",
      "image_description": "Subtracting the common component leaves the D3 representation in the zero-sum plane",
      "video_link_line": 144,
      "context_before": [
        {
          "start_line": 132,
          "end_line": 138,
          "markdown": "```math\n\\begin{pmatrix}1\\\\3\\\\5\\end{pmatrix}\n=\n\\begin{pmatrix}3\\\\3\\\\3\\end{pmatrix}\n+\n\\begin{pmatrix}-2\\\\0\\\\2\\end{pmatrix}.\n```"
        },
        {
          "start_line": 140,
          "end_line": 140,
          "markdown": "The common part lies on the rotation axis and is unchanged by every permutation. The zero-sum part lies in a plane through the origin. Although written with three components, it needs only two independent numbers, since the third must make their sum zero."
        }
      ],
      "context_after": [
        {
          "start_line": 148,
          "end_line": 148,
          "markdown": "We also notice that vectors that lie on the axis of rotation itself are left unchanged by the transformation. A way to think of this is to allow the triangle's vertices to store some information, like a number or any numerical quantity. If the value they store is the same for all vertices, the symmetry actions have no effect, whereas if they are different, the actions permute those values in a way that can be represented in a 2-dimensional vector space. The 3-dimensional representation space we began with is thus decomposable into 2 subspaces, one 1-dimensional, the other 2-dimensional. These cannot be decomposed further. That is, there is no lower-dimensional space such that an allowable transformation of any state remains in that space. The 1-dimensional and 2-dimensional representations are called **irreducible representations** or **irreps** for short. This is admittedly heavy math. Why do we bother? In the story we have to tell of quantum physics, where constituents of matter must abide the dynamical symmetry of nature, each constituent, each type of **particle** such as electron or photon, corresponds to an irreducible representation of nature's symmetry. A particular state of the particle is encoded in an element in the corresponding irrep."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_d3_irrep_collapse_animation.py",
        "scripts/symmetry_d3_rendering.py"
      ],
      "generation_note": "Run the main script without arguments. It uses symmetry_d3_rendering for shared geometry, transformations, and drawing, then encodes its frames with FFmpeg. The imported module is a code dependency, not an input movie.",
      "evidence": "The generator explicitly writes symmetry-d3-irrep-collapse.mp4; constants specify 1200 x 720 at 24 fps. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_d3_irrep_collapse_animation.py",
          "file": "sources/scripts/generate_symmetry_d3_irrep_collapse_animation.py.txt",
          "sha256": "b2b485ff2947a4c36a48e61f74e22e1ecc8c28c9b1f669360b86cdaeaa99c1c1",
          "bytes": 10237,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_d3_irrep_collapse_animation.py"
        },
        {
          "path": "scripts/symmetry_d3_rendering.py",
          "file": "sources/scripts/symmetry_d3_rendering.py.txt",
          "sha256": "4be9f7169cfe3654b9a1457de70a65e29dd20cc4863c7445a5ca6155f4453d6c",
          "bytes": 13034,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/symmetry_d3_rendering.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-d3-irrep-collapse/manifest.json",
      "manifest_sha256": "733b70a9c9c9eff7864345e8478177b4b06ea316c617c0c79f8988431ecb7502",
      "frame_directory": "symmetry-d3-irrep-collapse/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-d3-irrep-collapse.mp4",
        "video_sha256": "74829beaa2e554b3dc0a76253f7eaec8b5360cfbb3efab93945ef587ba80fb77",
        "duration_seconds": 23.75,
        "fps": 24.0,
        "frame_count": 570,
        "width": 1200,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "fadba2cb92e34453b89be0aa5ad7d24513a33ab01e6305337cb2ffbba0344074"
          },
          {
            "file": "frame-02.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "2b9818a9d939fc2b5e05d74b5b116d2a09a0a03b37904068e1eb8fef0eed0b8a"
          },
          {
            "file": "frame-03.jpg",
            "index": 103,
            "time_seconds": 4.291667,
            "sha256": "40b78a5cdc8511d413da91aec72a303329794725b1cf3863be41c82ec985423c"
          },
          {
            "file": "frame-04.jpg",
            "index": 155,
            "time_seconds": 6.458333,
            "sha256": "648e813d6cb2e9160e6e5c572ca0e979b2ca33088093862832f45a8ceec0341e"
          },
          {
            "file": "frame-05.jpg",
            "index": 207,
            "time_seconds": 8.625,
            "sha256": "d88d58e3d135fa2c773dd27afdfad4879683b967944dfe6b24090417c9868242"
          },
          {
            "file": "frame-06.jpg",
            "index": 259,
            "time_seconds": 10.791667,
            "sha256": "939c7cf5ccf542f256a05a7f09593c888d5ae905a6bf43fca791159086b8c947"
          },
          {
            "file": "frame-07.jpg",
            "index": 310,
            "time_seconds": 12.916667,
            "sha256": "7e5b1b1daab91027694f5a60397c1268f7963707c1fff58850cb4d002f472f62"
          },
          {
            "file": "frame-08.jpg",
            "index": 362,
            "time_seconds": 15.083333,
            "sha256": "8ce20107a08c9db50d47130d74118f245d0caabdbcae43a6a0189086d793c2b2"
          },
          {
            "file": "frame-09.jpg",
            "index": 414,
            "time_seconds": 17.25,
            "sha256": "7784837d4515e1d649bb1b021db2cb8e71a5fe543064955b2ea929e564e3c8e8"
          },
          {
            "file": "frame-10.jpg",
            "index": 466,
            "time_seconds": 19.416667,
            "sha256": "553cb695d5f052d42fc1e76a2cd2b76116bbbd74eef2ae219e18a97b6354d74c"
          },
          {
            "file": "frame-11.jpg",
            "index": 517,
            "time_seconds": 21.541667,
            "sha256": "e7b24f3befcbb9dff0a55b62c859bd48d6eba8eb63ea69a025469523d437441e"
          },
          {
            "file": "frame-12.jpg",
            "index": 569,
            "time_seconds": 23.708333,
            "sha256": "5d0bc68e61c6f6d187fae1c950ed5d3f2d257ab9c03d7140292816145ec67d4b"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "2f7bb9f6a07b7412d9f0c8852797997528233cdf139b069fea9f04abf800cbcd"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-d3-irrep-collapse",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-d3-irrep-collapse",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-d3-irrep-collapse.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-d3-irrep-collapse/"
    },
    {
      "video": "content/drafts/animations/symmetry-continuous-so2-translation.mp4",
      "original_reference": "animations/symmetry-continuous-so2-translation.mp4",
      "basename": "symmetry-continuous-so2-translation.mp4",
      "slug": "symmetry-continuous-so2-translation",
      "chapter_section": "Continuous symmetries",
      "caption": "Continuous symmetries",
      "image_description": "Continuous rotation and translation symmetries",
      "video_link_line": 175,
      "context_before": [
        {
          "start_line": 171,
          "end_line": 171,
          "markdown": "What rotations return a circle to itself? All of them! Similarly, all translations return a line to itself. The symmetries of space and time are continuous and can therefore be represented as transformations that depend on continuously varying parameters. "
        }
      ],
      "context_after": [],
      "sources": [
        "scripts/generate_symmetry_continuous_so2_translation_animation.py",
        "scripts/_make_contact_sheets.py"
      ],
      "generation_note": "Run the main script without arguments. It draws and encodes the movie directly, then calls make_contact_sheet from _make_contact_sheets to sample the encoded output. The helper does not supply an earlier input movie.",
      "evidence": "main() explicitly writes symmetry-continuous-so2-translation.mp4; constants specify 1280 x 620, 24 fps, 120 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_continuous_so2_translation_animation.py",
          "file": "sources/scripts/generate_symmetry_continuous_so2_translation_animation.py.txt",
          "sha256": "217746be53dc44cb1ded70680fa654bc660f39e594789bdd0807255fb2d2c721",
          "bytes": 6925,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_continuous_so2_translation_animation.py"
        },
        {
          "path": "scripts/_make_contact_sheets.py",
          "file": "sources/scripts/_make_contact_sheets.py.txt",
          "sha256": "9084b8285991d44dcba3ba937cc55ac76d7bc761c9c9cf048d2735cc1f6eadb9",
          "bytes": 4185,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/_make_contact_sheets.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-continuous-so2-translation/manifest.json",
      "manifest_sha256": "109dc4b4e0016ca26fbb269cd9b87b6762761da809a4e60206baea9f01d25d4e",
      "frame_directory": "symmetry-continuous-so2-translation/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-continuous-so2-translation.mp4",
        "video_sha256": "63f62f9a684091f29dea57b738da51ac054b51f81acf29a63aa83a4b1b346381",
        "duration_seconds": 5.0,
        "fps": 24.0,
        "frame_count": 120,
        "width": 1280,
        "height": 620,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "42e023398c3d3152846e09fcb9ab78da9a5cbc8d66b73921fdfa424bc413c385"
          },
          {
            "file": "frame-02.jpg",
            "index": 11,
            "time_seconds": 0.458333,
            "sha256": "ad6672ac9e6fa2a43a4f2752eb58019dfb66c9b31b718604b90fa8e410c286ad"
          },
          {
            "file": "frame-03.jpg",
            "index": 22,
            "time_seconds": 0.916667,
            "sha256": "f23fecec15a20db246d9738731a056379bdb2e9d692142245c46cd9e4631c511"
          },
          {
            "file": "frame-04.jpg",
            "index": 32,
            "time_seconds": 1.333333,
            "sha256": "1a9c25fd25593446f33bc21c26c197636df23bb6121f2bba93e6e62749d75b60"
          },
          {
            "file": "frame-05.jpg",
            "index": 43,
            "time_seconds": 1.791667,
            "sha256": "a20b7798e3a9266828744b8ec8eda52942f38c3d6d052a0a1417c41f773ecf1f"
          },
          {
            "file": "frame-06.jpg",
            "index": 54,
            "time_seconds": 2.25,
            "sha256": "4a5658aa2b61f4eedc0a51254efee9d4b2919f910ceb8dddaafd94ddef49d7e4"
          },
          {
            "file": "frame-07.jpg",
            "index": 65,
            "time_seconds": 2.708333,
            "sha256": "98e5251baf6bad2a0c41f6dbd284011e651b95055cac5db5bc85389fe3412d74"
          },
          {
            "file": "frame-08.jpg",
            "index": 76,
            "time_seconds": 3.166667,
            "sha256": "8335770573f8f2515927d31cfc4d7ba2bc1102acc765a8971547716a66f169fb"
          },
          {
            "file": "frame-09.jpg",
            "index": 87,
            "time_seconds": 3.625,
            "sha256": "73fb65bc1ed24a02ca104f5a959f90d99da77fcc1657a6432ed91f7a981ec8b6"
          },
          {
            "file": "frame-10.jpg",
            "index": 97,
            "time_seconds": 4.041667,
            "sha256": "6669463dbba296d4fde8b6feee8765e85807a8001c4603a8aa7133f1364ccf7a"
          },
          {
            "file": "frame-11.jpg",
            "index": 108,
            "time_seconds": 4.5,
            "sha256": "27897effe0cdc7f134265549e30470568ca64d351ed930c08b2588e0a9828c07"
          },
          {
            "file": "frame-12.jpg",
            "index": 119,
            "time_seconds": 4.958333,
            "sha256": "73a40106aa72e85469bc660fce030e4ec1c312701e54f16a9da64c1b9ac16ca1"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "d4dfe30aae4b363b252c211a0dd7e16d0192613773af188a28895cce903ef8c3"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-continuous-so2-translation",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-continuous-so2-translation",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-continuous-so2-translation.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-continuous-so2-translation/"
    },
    {
      "video": "content/drafts/animations/symmetry-so2-vector-field-flow.mp4",
      "original_reference": "animations/symmetry-so2-vector-field-flow.mp4",
      "basename": "symmetry-so2-vector-field-flow.mp4",
      "slug": "symmetry-so2-vector-field-flow",
      "chapter_section": "Symmetry Flows",
      "caption": "Symmetry flows provide a rich picture of symmetry transformations",
      "image_description": "Rotation generator as a vector field",
      "video_link_line": 334,
      "context_before": [
        {
          "start_line": 330,
          "end_line": 330,
          "markdown": "We can think of a symmetry transform as a \"uniform looking\" vector field, as illustrated below:"
        }
      ],
      "context_after": [
        {
          "start_line": 338,
          "end_line": 338,
          "markdown": "Here we ask not what happens when the transformation is applied to a single set of starting conditions, but what the transformation does to all starting conditions. This view lends itself to seeing the structure in the evolution of a collection of nearby states."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_so2_vector_field_flow_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the vector-field-flow frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "main() explicitly writes symmetry-so2-vector-field-flow.mp4; constants specify 1280 x 720, 24 fps, 210 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_so2_vector_field_flow_animation.py",
          "file": "sources/scripts/generate_symmetry_so2_vector_field_flow_animation.py.txt",
          "sha256": "a966dd1533d8aa529a7412b9a72cf12d3a118d5cf54fc5e1f80c348e82b8054c",
          "bytes": 10752,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_so2_vector_field_flow_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-so2-vector-field-flow/manifest.json",
      "manifest_sha256": "7937cf4346d50a56ceb530738b831397e538518bd602ff6be9a26a4cd6d600cd",
      "frame_directory": "symmetry-so2-vector-field-flow/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-so2-vector-field-flow.mp4",
        "video_sha256": "8eb47d9c4cc2c5949c9c02c0ae622fc244ea9203c9fe3aa880f1e64e5e51057a",
        "duration_seconds": 8.75,
        "fps": 24.0,
        "frame_count": 210,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "f99c1d3a3ff3a92876dada99715ea5d8907826756d260fa84ffaba117bed6daa"
          },
          {
            "file": "frame-02.jpg",
            "index": 19,
            "time_seconds": 0.791667,
            "sha256": "38a835a3fc8a5a5cd5fd76645f3ecda1bb813cf936ede05b9f065cfaef8c5c94"
          },
          {
            "file": "frame-03.jpg",
            "index": 38,
            "time_seconds": 1.583333,
            "sha256": "22033d054b90f874df07fe3034a50511215fe827e0a1318a00fc240f4766b481"
          },
          {
            "file": "frame-04.jpg",
            "index": 57,
            "time_seconds": 2.375,
            "sha256": "a87443e0f18e5f201047e9bc734604496cb1858401060b42a50e43097865bc9c"
          },
          {
            "file": "frame-05.jpg",
            "index": 76,
            "time_seconds": 3.166667,
            "sha256": "3a6e52909fc2fd4714ba4c9e48e2750713e1ef8d48f405232679351c2313493a"
          },
          {
            "file": "frame-06.jpg",
            "index": 95,
            "time_seconds": 3.958333,
            "sha256": "9721ffa660dd7afa15cc495fe836d1f23418d7972b33d3d1f1208c17300d7b52"
          },
          {
            "file": "frame-07.jpg",
            "index": 114,
            "time_seconds": 4.75,
            "sha256": "f11cc6025bb8800ac531ce89fa8b4cf4ec1f23863141a1edc598fc693553733b"
          },
          {
            "file": "frame-08.jpg",
            "index": 133,
            "time_seconds": 5.541667,
            "sha256": "a663cfe43ed9c47c022d296ae2b6c81fdcb21cf98b56d68fefd10608dbec43ed"
          },
          {
            "file": "frame-09.jpg",
            "index": 152,
            "time_seconds": 6.333333,
            "sha256": "c54920fe70952f17454e158be42a2e374403a0baf3bfae7e177dc158bd998b90"
          },
          {
            "file": "frame-10.jpg",
            "index": 171,
            "time_seconds": 7.125,
            "sha256": "e8c1125364f7bd902ce5c0f0b027af26ed0dc99cbbdc683885e8d3d536e45e1b"
          },
          {
            "file": "frame-11.jpg",
            "index": 190,
            "time_seconds": 7.916667,
            "sha256": "5819ce8e3a0b0b9a14aee98af7146897796f981a00e0d2794d4789c42a5ec313"
          },
          {
            "file": "frame-12.jpg",
            "index": 209,
            "time_seconds": 8.708333,
            "sha256": "e6322f218eacf0bd73c6641e2392727fc0f0a1165851124486e1c5d6ab9ff1cc"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "4271435b1a89e0f55f7ca86c854c09515bc8fc4626cb32c918f834e9525a56c9"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-so2-vector-field-flow",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-so2-vector-field-flow",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-so2-vector-field-flow.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-so2-vector-field-flow/"
    },
    {
      "video": "content/drafts/animations/symmetry-rotation-vector-invariants.mp4",
      "original_reference": "animations/symmetry-rotation-vector-invariants.mp4",
      "basename": "symmetry-rotation-vector-invariants.mp4",
      "slug": "symmetry-rotation-vector-invariants",
      "chapter_section": "Invariants and Metrics",
      "caption": "Metric structure",
      "image_description": "Rotation preserves vector lengths and angles",
      "video_link_line": 345,
      "context_before": [
        {
          "start_line": 341,
          "end_line": 341,
          "markdown": "What is the invariant of rotation in this representation? It is just the length of vectors and the angles between them."
        }
      ],
      "context_after": [
        {
          "start_line": 349,
          "end_line": 349,
          "markdown": "These relationships are expressed by a single invariant, the dot product."
        },
        {
          "start_line": 351,
          "end_line": 355,
          "markdown": "```math\n\\mathbf u\\cdot\\mathbf v\n=\nu_xv_x+u_yv_y.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_rotation_vector_invariants_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the rotation-and-invariants frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "main() explicitly writes symmetry-rotation-vector-invariants.mp4; constants specify 1280 x 720, 24 fps, 144 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_rotation_vector_invariants_animation.py",
          "file": "sources/scripts/generate_symmetry_rotation_vector_invariants_animation.py.txt",
          "sha256": "c8067f8068fa97e6ec940af9a1b55b37c5beefb2c9f8083eba06d97155ec5b60",
          "bytes": 6973,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_rotation_vector_invariants_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-rotation-vector-invariants/manifest.json",
      "manifest_sha256": "332ab5d4cfa230c54457c56c0e65b95a5b2c028b94b308fb183ce53b0bd1e755",
      "frame_directory": "symmetry-rotation-vector-invariants/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-rotation-vector-invariants.mp4",
        "video_sha256": "f82c316a58b99f9882752b4074fbb5fca732796d667707c321812c9e0fcff340",
        "duration_seconds": 6.0,
        "fps": 24.0,
        "frame_count": 144,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "8022efca2db771d33966655e931c321e31c12343db6fc25ba2a61889eb672142"
          },
          {
            "file": "frame-02.jpg",
            "index": 13,
            "time_seconds": 0.541667,
            "sha256": "3400251694d5f6d8a507d85483622f96ce1ad11ea629c011d608bda319cea335"
          },
          {
            "file": "frame-03.jpg",
            "index": 26,
            "time_seconds": 1.083333,
            "sha256": "153b663cfbe19ce38c6f9001b382424849632e765ed73409302556ae13e2c5ab"
          },
          {
            "file": "frame-04.jpg",
            "index": 39,
            "time_seconds": 1.625,
            "sha256": "530b9369d363ac2b44d738e0a9fda6b84bb6ad603c59728897fa5cd67e6a47bc"
          },
          {
            "file": "frame-05.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "9936e33daaa54eb6f27ece11bdf7791f3441585b7c157c3409baca90d16b48b6"
          },
          {
            "file": "frame-06.jpg",
            "index": 65,
            "time_seconds": 2.708333,
            "sha256": "a8450a0348992bd9c3b1ad230841fc960dac0ddeb826cdb4430cc99ba0646a80"
          },
          {
            "file": "frame-07.jpg",
            "index": 78,
            "time_seconds": 3.25,
            "sha256": "9164ddd9dc579739d3ea8e09ca9e57c714a8ff98cdab28c20ac9d536df17fb60"
          },
          {
            "file": "frame-08.jpg",
            "index": 91,
            "time_seconds": 3.791667,
            "sha256": "7dae73732e102d269752c8b179ade2c905f44fd241f84b6403b7aa27ca7ecd21"
          },
          {
            "file": "frame-09.jpg",
            "index": 104,
            "time_seconds": 4.333333,
            "sha256": "4090238c70ba4a47a024c98cd0a45917f4c4f7399bf02d60600b005d069ee890"
          },
          {
            "file": "frame-10.jpg",
            "index": 117,
            "time_seconds": 4.875,
            "sha256": "4675dc96ad601dfcc2ba536e0e9e05960cadbe51042399f87f2304cf504b87fc"
          },
          {
            "file": "frame-11.jpg",
            "index": 130,
            "time_seconds": 5.416667,
            "sha256": "50a128e86bbc79c79fe52ab2db70005f3fd1b1abbf0f32dd2f4f83ebf4b767d9"
          },
          {
            "file": "frame-12.jpg",
            "index": 143,
            "time_seconds": 5.958333,
            "sha256": "abd49464ae7779e6032cd0575e17a95a318cc8c606a87b4f8acb44ea2971c194"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "ff66b8cf0b1badee7273fd96bd64e34a4574e4194132652ba0ff70a2e7401cc6"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-rotation-vector-invariants",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-rotation-vector-invariants",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-rotation-vector-invariants.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-rotation-vector-invariants/"
    },
    {
      "video": "content/drafts/animations/symmetry-hyperbolic-rotation.mp4",
      "original_reference": "animations/symmetry-hyperbolic-rotation.mp4",
      "basename": "symmetry-hyperbolic-rotation.mp4",
      "slug": "symmetry-hyperbolic-rotation",
      "chapter_section": "Invariants and Metrics",
      "caption": "Hyperbolic rotations",
      "image_description": "Hyperbolic rotation preserving the interval",
      "video_link_line": 361,
      "context_before": [
        {
          "start_line": 351,
          "end_line": 355,
          "markdown": "```math\n\\mathbf u\\cdot\\mathbf v\n=\nu_xv_x+u_yv_y.\n```"
        },
        {
          "start_line": 357,
          "end_line": 357,
          "markdown": "An invariant of this sort, that fixes some notion of placing reliable rulers on the space, is a **metric**. For normal rotations and translations, these relationships are intuitive, but a symmetry may preserve a less intuitive metric. If we define invariant length not as \\(x^2+y^2\\) but as \\(t^2-\\mathbf{x}^2\\), we have a \"hyperbolic rotation.\" Separation between points stretches out to infinity as the rotation approaches its asymptotes, leaving the invariant interval unchanged even though the separation appears to grow using our everyday notion of distance. Such a hyperbolic metric, as we will see, encodes the asymptotic limit of light speed in the relativistic geometry **spacetime**."
        }
      ],
      "context_after": [],
      "sources": [
        "scripts/generate_symmetry_hyperbolic_rotation_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the hyperbolic-rotation frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "main() explicitly writes symmetry-hyperbolic-rotation.mp4; constants specify 1280 x 720, 24 fps, 168 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_hyperbolic_rotation_animation.py",
          "file": "sources/scripts/generate_symmetry_hyperbolic_rotation_animation.py.txt",
          "sha256": "cea7f9d590e665552c5bde31f6726efa6b61a2e1d50d9eac4745baada9e86903",
          "bytes": 8561,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_hyperbolic_rotation_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-hyperbolic-rotation/manifest.json",
      "manifest_sha256": "c786aca663d1dfbef32f333b353300210c31459fdff04dea6f92c333e869f65a",
      "frame_directory": "symmetry-hyperbolic-rotation/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-hyperbolic-rotation.mp4",
        "video_sha256": "17517c198f57608fce58c372da980248bd7ea5bece69ceef0b96da3622742b14",
        "duration_seconds": 7.0,
        "fps": 24.0,
        "frame_count": 168,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "62f4d38a46b2b804205ef4dc37ae0e05531f730f18b0ab00a71df17645ecee8c"
          },
          {
            "file": "frame-02.jpg",
            "index": 15,
            "time_seconds": 0.625,
            "sha256": "72930e08646ae614e33177f74c043ef686d4c6d7d6f56a2262a74b220db5b231"
          },
          {
            "file": "frame-03.jpg",
            "index": 30,
            "time_seconds": 1.25,
            "sha256": "35623aef38c8bf51c6f1a441118a45cfdc0d31fae32374f4792da0583f52fa0e"
          },
          {
            "file": "frame-04.jpg",
            "index": 46,
            "time_seconds": 1.916667,
            "sha256": "6042f435cb8d76353afea5a64be3e577073fcce5cc54e971604af21217dc526b"
          },
          {
            "file": "frame-05.jpg",
            "index": 61,
            "time_seconds": 2.541667,
            "sha256": "57d3e8ec94eeed2c17fa612d3d80eae40873e14f3568dc68e6bee89c9816c20a"
          },
          {
            "file": "frame-06.jpg",
            "index": 76,
            "time_seconds": 3.166667,
            "sha256": "e80526c47521958b22d043a8bb1a53c82d3245bb0c9cf4149295b69b0603dd90"
          },
          {
            "file": "frame-07.jpg",
            "index": 91,
            "time_seconds": 3.791667,
            "sha256": "7719895e62be32787bcd3185e5222ea8b90d8bdb2c37ec79c8f2b46ed07fc245"
          },
          {
            "file": "frame-08.jpg",
            "index": 106,
            "time_seconds": 4.416667,
            "sha256": "608abd50ec1189878fc4ad0b70e5cdc31038374e0667a9f8989ceb5b80bab79e"
          },
          {
            "file": "frame-09.jpg",
            "index": 121,
            "time_seconds": 5.041667,
            "sha256": "63fee49525a5a19750c8d6b8ad5e24cfccf1467a97c36bafe679d1f8513bdffb"
          },
          {
            "file": "frame-10.jpg",
            "index": 137,
            "time_seconds": 5.708333,
            "sha256": "e541060dfe60ac2810b371d4a7ebab9db5ffb56686d1bac428f27daf322a3cd5"
          },
          {
            "file": "frame-11.jpg",
            "index": 152,
            "time_seconds": 6.333333,
            "sha256": "43ef288b5e97199c0e49002b0d8943665a58773ec42b34b2cab157b3d7106e6d"
          },
          {
            "file": "frame-12.jpg",
            "index": 167,
            "time_seconds": 6.958333,
            "sha256": "be6f2edef8440a28d84bf977f64837cb760b4fa556b53f9e6900d6106adb16bd"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "a204e19d7fe691a86ae861c5c2b0f4a29dcd51c541a34cf550b18148e6d44c17"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-hyperbolic-rotation",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-hyperbolic-rotation",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-hyperbolic-rotation.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-hyperbolic-rotation/"
    },
    {
      "video": "content/drafts/animations/symmetry-so3-rotation-order.mp4",
      "original_reference": "animations/symmetry-so3-rotation-order.mp4",
      "basename": "symmetry-so3-rotation-order.mp4",
      "slug": "symmetry-so3-rotation-order",
      "chapter_section": "Commutators",
      "caption": "Non-commuting operations of $SO_3$",
      "image_description": "Noncommuting 90-degree rotations in three dimensions",
      "video_link_line": 370,
      "context_before": [
        {
          "start_line": 366,
          "end_line": 366,
          "markdown": "In the case of a single continuous symmetry transformation, the generator combined with a parameter, like angle in the case of rotation, specifies an arbitrary transformation. However, when there are multiple independent transformations that can be composed to form a multi-dimensional group, the order of the composition may also need to be taken into account because it can produce different resultant states. For example, in the 3-dimensional rotation group, called $SO(3)$, rotating about the $x$-axis then the $y$-axis leaves a sphere in a different state than applying the same actions in the opposite order."
        }
      ],
      "context_after": [
        {
          "start_line": 374,
          "end_line": 374,
          "markdown": "This order-dependence is expressed by the **commutator**:"
        },
        {
          "start_line": 376,
          "end_line": 380,
          "markdown": "```math\n[X,Y]\n=\nXY-YX.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_so3_rotation_order_animation.py",
        "scripts/symmetry_d3_rendering.py"
      ],
      "generation_note": "Run the main script without arguments. It uses symmetry_d3_rendering for shared geometry and drawing helpers, then encodes the ordered-rotation frames with FFmpeg. The imported module is a code dependency, not an input movie.",
      "evidence": "render() sets the output stem to symmetry-so3-rotation-order; constants specify 1200 x 700, 24 fps, 192 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_so3_rotation_order_animation.py",
          "file": "sources/scripts/generate_symmetry_so3_rotation_order_animation.py.txt",
          "sha256": "0f0602e6786fef7c245fe99e5fe007f2b6dec6176b9960d1c68c0689d3c66a45",
          "bytes": 13202,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_so3_rotation_order_animation.py"
        },
        {
          "path": "scripts/symmetry_d3_rendering.py",
          "file": "sources/scripts/symmetry_d3_rendering.py.txt",
          "sha256": "4be9f7169cfe3654b9a1457de70a65e29dd20cc4863c7445a5ca6155f4453d6c",
          "bytes": 13034,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/symmetry_d3_rendering.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-so3-rotation-order/manifest.json",
      "manifest_sha256": "b4e3acf946a62dace7bb7fbb7f5c75bd434757353b23872ac907e78bbfdd4942",
      "frame_directory": "symmetry-so3-rotation-order/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-so3-rotation-order.mp4",
        "video_sha256": "ded432ba59e3b6c415ee80644f09c2e15b7b94d57762df86a6d54f12e90bfdf0",
        "duration_seconds": 8.0,
        "fps": 24.0,
        "frame_count": 192,
        "width": 1200,
        "height": 700,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "43c8ebf3f5146fb64ca0209bde7c71e6d1f2cb33823c383aafd807b8c015786d"
          },
          {
            "file": "frame-02.jpg",
            "index": 17,
            "time_seconds": 0.708333,
            "sha256": "0c888f26f3bdf86843fca310f0407ba669da5aabd28d3f8053b02e72d1b9a5c8"
          },
          {
            "file": "frame-03.jpg",
            "index": 35,
            "time_seconds": 1.458333,
            "sha256": "8d5d2de89533ccff69a9cb66c21183a154d9d93b1dde8ca7c5f9e10dd1ec6cb2"
          },
          {
            "file": "frame-04.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "4bbdfee63d73266b0b408ad692e0c96c841cc0920ff070a4a0ea3d6b388ec602"
          },
          {
            "file": "frame-05.jpg",
            "index": 69,
            "time_seconds": 2.875,
            "sha256": "86fd912c2627dd8023df92629f798fe6097e954a7e7a0059cadfa3bda241b71f"
          },
          {
            "file": "frame-06.jpg",
            "index": 87,
            "time_seconds": 3.625,
            "sha256": "71ddf37dd8ad9c355f7033964aca9bfdeff6b574653d1a7a5f088679c7014786"
          },
          {
            "file": "frame-07.jpg",
            "index": 104,
            "time_seconds": 4.333333,
            "sha256": "83b18f910c816d92184895e332f2a62d0a99ac34d230da1d4e36b189d136d8e9"
          },
          {
            "file": "frame-08.jpg",
            "index": 122,
            "time_seconds": 5.083333,
            "sha256": "4e6a7f1953ca3a23ccbb33c51dbcc335b6820cdbd0f862d20a479a456510291b"
          },
          {
            "file": "frame-09.jpg",
            "index": 139,
            "time_seconds": 5.791667,
            "sha256": "8834138c92959a985ab8fbe859f269a5ef0637baf9538bb72e76c64d6d9987b3"
          },
          {
            "file": "frame-10.jpg",
            "index": 156,
            "time_seconds": 6.5,
            "sha256": "8d08e98bdf7a7703bb8dccdf9b0ac3de7baf3fc2fbb55528b3e1762ccf04fb19"
          },
          {
            "file": "frame-11.jpg",
            "index": 174,
            "time_seconds": 7.25,
            "sha256": "ec398b1ff296b333d6f80f111100b38e6265b4752ab10294c5b6b9cf691b015b"
          },
          {
            "file": "frame-12.jpg",
            "index": 191,
            "time_seconds": 7.958333,
            "sha256": "35bb2e137327211aec153dd300c1e21461ccfaeca2b40a0cc7bee6edeae09cd1"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "038843240257ea91dabf4910ee9c070c9481e261df51eeb5824625d7646ac8a2"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-so3-rotation-order",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-so3-rotation-order",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-so3-rotation-order.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-so3-rotation-order/"
    },
    {
      "video": "content/drafts/animations/symmetry-translation-point-linearity-failure.mp4",
      "original_reference": "animations/symmetry-translation-point-linearity-failure.mp4",
      "basename": "symmetry-translation-point-linearity-failure.mp4",
      "slug": "symmetry-translation-point-linearity-failure",
      "chapter_section": "Translations and Function Representation",
      "caption": "Shifting a number line is not a linear operation",
      "image_description": "Point translation failing linearity",
      "video_link_line": 517,
      "context_before": [
        {
          "start_line": 505,
          "end_line": 511,
          "markdown": "```math\nT_a(x_1)+T_a(x_2)\n=\n(x_1+a)+(x_2+a)\n=\nx_1+x_2+2a.\n```"
        },
        {
          "start_line": 513,
          "end_line": 513,
          "markdown": "These are not equal unless $a=0$. Translation does act sensibly on a number line, but it is not a linear operation when acting on this space."
        }
      ],
      "context_after": [
        {
          "start_line": 521,
          "end_line": 521,
          "markdown": "On the other hand, the space of functions on a number line does form a linear representation. Instead of translating the point $x$, we translate a function by shifting its argument:"
        },
        {
          "start_line": 523,
          "end_line": 525,
          "markdown": "```math\n(T_a f)(x)=f(x-a).\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_translation_linearity_animations.py"
      ],
      "generation_note": "Run the main script without arguments. It renders two separate movies: this point-translation example using draw_point_linearity_frame, and the function-linearity example using its other renderer. Frames are drawn with Pillow and encoded with FFmpeg; no local Python imports or input movie are used.",
      "evidence": "main() pairs the exact symmetry-translation-point-linearity-failure stem with draw_point_linearity_frame. Constants specify 1280 x 720, 24 fps, 156 frames. No matching validation JSON is present; attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_translation_linearity_animations.py",
          "file": "sources/scripts/generate_symmetry_translation_linearity_animations.py.txt",
          "sha256": "4f89641cae5f356ac6ad2f3606ec88eaf23611baeb0c98f81dcf3fbb920c436b",
          "bytes": 13361,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_translation_linearity_animations.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-translation-point-linearity-failure/manifest.json",
      "manifest_sha256": "1c26126304f7b9943440d25c26e81b605f6077484b79956f1462abdfb7ff4307",
      "frame_directory": "symmetry-translation-point-linearity-failure/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-translation-point-linearity-failure.mp4",
        "video_sha256": "8e71aef46dd223bb43ca609758f270c3867484b936e193b4f31d6cc60093123c",
        "duration_seconds": 6.5,
        "fps": 24.0,
        "frame_count": 156,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "76af039a9332e14342190847e7690a561485f16c24252c4633284215feeb96aa"
          },
          {
            "file": "frame-02.jpg",
            "index": 14,
            "time_seconds": 0.583333,
            "sha256": "8b4911e30c8ad00273af6fc07a5d12f2b1cf752a9ca19e5bc631871a2d4200d7"
          },
          {
            "file": "frame-03.jpg",
            "index": 28,
            "time_seconds": 1.166667,
            "sha256": "d4200851cdab50b8b8680aa252162ef41d82e765e5f17170c0750aad7aefabf7"
          },
          {
            "file": "frame-04.jpg",
            "index": 42,
            "time_seconds": 1.75,
            "sha256": "02fbfc5ec30702ea1c957a07c51c33eb8b7363cd9a7608a6bf0dc20cbe165fb6"
          },
          {
            "file": "frame-05.jpg",
            "index": 56,
            "time_seconds": 2.333333,
            "sha256": "9d48f721a6933e0419495e99f3ef511821722b51458da32d638c9d359f06197b"
          },
          {
            "file": "frame-06.jpg",
            "index": 70,
            "time_seconds": 2.916667,
            "sha256": "767fb9fc17e80fa8ea9ca52e049fb3de70ba30553049341031421e87dd30ad87"
          },
          {
            "file": "frame-07.jpg",
            "index": 85,
            "time_seconds": 3.541667,
            "sha256": "6f59324c08bb873be0ffab3c67cfbe499acd7b2987cfc030e9cba1a455af698a"
          },
          {
            "file": "frame-08.jpg",
            "index": 99,
            "time_seconds": 4.125,
            "sha256": "d24b79fbb5341b20db720ad1e789d488fa65b4f8fdfcdebc3fdaa70c1d9aab79"
          },
          {
            "file": "frame-09.jpg",
            "index": 113,
            "time_seconds": 4.708333,
            "sha256": "6836681d2f560f47c652d5148f7fbf0164131da2b10f986f7fb58cdabb3601a2"
          },
          {
            "file": "frame-10.jpg",
            "index": 127,
            "time_seconds": 5.291667,
            "sha256": "a533a9455997c9e9f70caaacaa7bb78abcdb4aa8fad472fd10f70edd07b30119"
          },
          {
            "file": "frame-11.jpg",
            "index": 141,
            "time_seconds": 5.875,
            "sha256": "15816a5e2846b0c15dc1a8a75f5bfbff30c62007ef02be0ffc153605d4881c00"
          },
          {
            "file": "frame-12.jpg",
            "index": 155,
            "time_seconds": 6.458333,
            "sha256": "15816a5e2846b0c15dc1a8a75f5bfbff30c62007ef02be0ffc153605d4881c00"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "348b13818bc0e8a8a72226933ab55d3f2b2454cb8403197128aaa3980415c550"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-translation-point-linearity-failure",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-translation-point-linearity-failure",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-point-linearity-failure.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-translation-point-linearity-failure/"
    },
    {
      "video": "content/drafts/animations/symmetry-translation-function-linearity.mp4",
      "original_reference": "animations/symmetry-translation-function-linearity.mp4",
      "basename": "symmetry-translation-function-linearity.mp4",
      "slug": "symmetry-translation-function-linearity",
      "chapter_section": "Translations and Function Representation",
      "caption": "A function representation is linear",
      "image_description": "Function translation preserving linearity",
      "video_link_line": 557,
      "context_before": [
        {
          "start_line": 549,
          "end_line": 549,
          "markdown": "The zero function is also fixed:"
        },
        {
          "start_line": 551,
          "end_line": 553,
          "markdown": "```math\nT_a0=0.\n```"
        }
      ],
      "context_after": [
        {
          "start_line": 561,
          "end_line": 561,
          "markdown": "This makes sense. Translational symmetry needs an object to translate, just as $D_3$ symmetry needs a triangly thing to translate. We can think of the function as a shape. If a system possesses translational symmetry, that shape is preserved."
        },
        {
          "start_line": 563,
          "end_line": 563,
          "markdown": "This describes how the function transforms. Whether the physical system it describes behaves the same way after translation is a separate question."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_translation_linearity_animations.py"
      ],
      "generation_note": "Run the main script without arguments. It renders two separate movies: this function-linearity example using draw_function_linearity_frame, and the point-translation example using its other renderer. Frames are drawn with Pillow and encoded with FFmpeg; no local Python imports or input movie are used.",
      "evidence": "main() pairs the exact symmetry-translation-function-linearity stem with draw_function_linearity_frame. Constants specify 1280 x 720, 24 fps, 156 frames. No matching validation JSON is present; attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_translation_linearity_animations.py",
          "file": "sources/scripts/generate_symmetry_translation_linearity_animations.py.txt",
          "sha256": "4f89641cae5f356ac6ad2f3606ec88eaf23611baeb0c98f81dcf3fbb920c436b",
          "bytes": 13361,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_translation_linearity_animations.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-translation-function-linearity/manifest.json",
      "manifest_sha256": "9e08ce375d9696f1a13a09e10d703b19a615146bef90da47bedf1db6b9934bf2",
      "frame_directory": "symmetry-translation-function-linearity/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-translation-function-linearity.mp4",
        "video_sha256": "0d4cfa151e7bb58d86655ba109f676b5ea411c53809f88a8f1862f6e06eaa503",
        "duration_seconds": 6.5,
        "fps": 24.0,
        "frame_count": 156,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "e73863bfca3afa239b57d2aad55e44afef5b80430cdb23295b425463048b3a16"
          },
          {
            "file": "frame-02.jpg",
            "index": 14,
            "time_seconds": 0.583333,
            "sha256": "e6093b6576a2f7ea1578f43fd2dece704248338c1bf13e2fe1a15e520b4116a0"
          },
          {
            "file": "frame-03.jpg",
            "index": 28,
            "time_seconds": 1.166667,
            "sha256": "1d466c59e41ba8598ca8c7b33a5da24cccace61eceb04fbe7bff274dca2d922e"
          },
          {
            "file": "frame-04.jpg",
            "index": 42,
            "time_seconds": 1.75,
            "sha256": "cdc0562187af2b2d7dba9d53b8ec32abd870d9abb57d5a685b2b620bd1d071db"
          },
          {
            "file": "frame-05.jpg",
            "index": 56,
            "time_seconds": 2.333333,
            "sha256": "b77c327a69fcf8af67d36c658d59074421840b6139c7c6e401828d272675687f"
          },
          {
            "file": "frame-06.jpg",
            "index": 70,
            "time_seconds": 2.916667,
            "sha256": "9248c654a8859a7aefd6f6b0469687e64bc4a6115d05966fb545f87e24501d46"
          },
          {
            "file": "frame-07.jpg",
            "index": 85,
            "time_seconds": 3.541667,
            "sha256": "7261cfcbde15c7bc2aff3d21b2ba72579559028291e31087c881afa944d583ef"
          },
          {
            "file": "frame-08.jpg",
            "index": 99,
            "time_seconds": 4.125,
            "sha256": "ccf53e34afc70ddde2c3a505db846314636a11df324c28d7cc643d9da27d0684"
          },
          {
            "file": "frame-09.jpg",
            "index": 113,
            "time_seconds": 4.708333,
            "sha256": "1b801f558b97c69354c1cc722875110bf27c64b77de8f97ed821f30d253196be"
          },
          {
            "file": "frame-10.jpg",
            "index": 127,
            "time_seconds": 5.291667,
            "sha256": "6072e2e2077c67b1239365826bb46391aa7788b8a294d3c03228db88cec0c81d"
          },
          {
            "file": "frame-11.jpg",
            "index": 141,
            "time_seconds": 5.875,
            "sha256": "5a16c81160d64b7e8e7f85b5ef4ccdd0f9298b970d560945fe7d6eb30d55156c"
          },
          {
            "file": "frame-12.jpg",
            "index": 155,
            "time_seconds": 6.458333,
            "sha256": "5dee8a29a039fdf292d003f9b25c93161350df0bc496b838d954f48a3e51ba60"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "1bcc3c4409a58d3f90163b2e8d54dff84a6f14f8afd482d60bea92214ab81cc7"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-translation-function-linearity",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-translation-function-linearity",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-function-linearity.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-translation-function-linearity/"
    },
    {
      "video": "content/drafts/animations/symmetry-function-translation-shape.mp4",
      "original_reference": "animations/symmetry-function-translation-shape.mp4",
      "basename": "symmetry-function-translation-shape.mp4",
      "slug": "symmetry-function-translation-shape",
      "chapter_section": "Translations and Function Representation",
      "caption": "Translation of functions preserves their shape",
      "image_description": "Translation preserving a function shape",
      "video_link_line": 567,
      "context_before": [
        {
          "start_line": 561,
          "end_line": 561,
          "markdown": "This makes sense. Translational symmetry needs an object to translate, just as $D_3$ symmetry needs a triangly thing to translate. We can think of the function as a shape. If a system possesses translational symmetry, that shape is preserved."
        },
        {
          "start_line": 563,
          "end_line": 563,
          "markdown": "This describes how the function transforms. Whether the physical system it describes behaves the same way after translation is a separate question."
        }
      ],
      "context_after": [
        {
          "start_line": 571,
          "end_line": 571,
          "markdown": "Indeed, this representation is completely analogous to our 3 dimensional representation of $D_3$. There a vertex contained a component value, here an input value does. Indeed, the $D_3$ representation is itself a function representation, with a domain that contains only three members and is cyclic. "
        }
      ],
      "sources": [
        "scripts/generate_symmetry_function_translation_shape_animation.py"
      ],
      "generation_note": "Run the main script without --poster-only to draw and encode the translated-function frames. FFmpeg is resolved from configuration, the local tool installation, PATH, or imageio-ffmpeg. No local Python imports or input movie are used.",
      "evidence": "main() explicitly writes symmetry-function-translation-shape.mp4; constants specify 1280 x 620, 24 fps, 132 frames. No matching validation JSON is present. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_function_translation_shape_animation.py",
          "file": "sources/scripts/generate_symmetry_function_translation_shape_animation.py.txt",
          "sha256": "b5196f7ba9384160fa221e5d85e30a9602ffecdab222a9dafc58ab4b412fd5dd",
          "bytes": 8852,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_function_translation_shape_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-function-translation-shape/manifest.json",
      "manifest_sha256": "46b89f119f19a8f4d4a201a822dac0a94bf1451e76b358405175d16fb46ff962",
      "frame_directory": "symmetry-function-translation-shape/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-function-translation-shape.mp4",
        "video_sha256": "70b781ea9a0259674c356828596b2f59ecf49ed5e6daee2fff5b9d0fe5bfe166",
        "duration_seconds": 5.5,
        "fps": 24.0,
        "frame_count": 132,
        "width": 1280,
        "height": 620,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "538540a15d319a395eea8345d8a10d1eed0b1775366eaa93f540f57090972e1d"
          },
          {
            "file": "frame-02.jpg",
            "index": 12,
            "time_seconds": 0.5,
            "sha256": "0e45a49e95c75736b62b24fae54edf615ae29ea17113935623dd29da56cd4c17"
          },
          {
            "file": "frame-03.jpg",
            "index": 24,
            "time_seconds": 1.0,
            "sha256": "ce1060b830b76cd7b352d1ba6c766327f0cad39e477a92a6d8a6a54d22f9a74d"
          },
          {
            "file": "frame-04.jpg",
            "index": 36,
            "time_seconds": 1.5,
            "sha256": "dda1a69676e5301762dfdbd7436d00a43cc669f662bd483542270155af3bfdae"
          },
          {
            "file": "frame-05.jpg",
            "index": 48,
            "time_seconds": 2.0,
            "sha256": "b489b14fee4d22b1c9b5b3a0b31108f4de0bafbfdb4bbab6f9e672f53faae27d"
          },
          {
            "file": "frame-06.jpg",
            "index": 60,
            "time_seconds": 2.5,
            "sha256": "ea2d753a5ee70cd636ee1a01b9e81ea5411467f538398f8856bc953ea33e22a8"
          },
          {
            "file": "frame-07.jpg",
            "index": 71,
            "time_seconds": 2.958333,
            "sha256": "b6d1162a1999348c2e8a12a85c35be81a30b2aa7edaac5b853944a58cb1a029f"
          },
          {
            "file": "frame-08.jpg",
            "index": 83,
            "time_seconds": 3.458333,
            "sha256": "cfc4a334b456dcfeb931053d3ae4158fab9ddee6b954aacfc93a3db92f6313a1"
          },
          {
            "file": "frame-09.jpg",
            "index": 95,
            "time_seconds": 3.958333,
            "sha256": "7ad1c8231c67b8528ec918e1f6cd1384e043bba4f2c3fa1c08579e173c65ce4e"
          },
          {
            "file": "frame-10.jpg",
            "index": 107,
            "time_seconds": 4.458333,
            "sha256": "fb44c1e96f0a58124d0307a36a62b691af9b0136ecea1aee725cfe1b100e3f75"
          },
          {
            "file": "frame-11.jpg",
            "index": 119,
            "time_seconds": 4.958333,
            "sha256": "bc44b8cfd2cca1e272fca59e14db7e7faba78f31819eac40afc276eec5f18cc7"
          },
          {
            "file": "frame-12.jpg",
            "index": 131,
            "time_seconds": 5.458333,
            "sha256": "1a91ec66fa5fafee84a9881e6f4092786612980ae929c5bd1108b61b0edcb235"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "813e792ae7bda8b11183d3acc3087787fad28fb19f96b2b413c1dab59f1ce85c"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-function-translation-shape",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-function-translation-shape",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-function-translation-shape.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-function-translation-shape/"
    },
    {
      "video": "content/drafts/animations/symmetry-translation-tangent-zoom.mp4",
      "original_reference": "animations/symmetry-translation-tangent-zoom.mp4",
      "basename": "symmetry-translation-tangent-zoom.mp4",
      "slug": "symmetry-translation-tangent-zoom",
      "chapter_section": "Generator of Translations",
      "caption": "A function value is translated by the function's slope",
      "image_description": "Tangent approximation under local zoom contact sheet",
      "video_link_line": 578,
      "context_before": [
        {
          "start_line": 574,
          "end_line": 574,
          "markdown": "We have a function representation. Now we want to find an infinitesimal generator that acts on functions to produce a translation. We can do so by considering what happens to a function's value under \"tiny\" displacements. In that case, the new value $f(x-a)$ is close to the old value $f(x)$, and correction is given, in the infinitesimal limit, by the slope at $x$. This is the same idea that any curve becomes flat when zoomed in sufficiently:"
        }
      ],
      "context_after": [
        {
          "start_line": 582,
          "end_line": 582,
          "markdown": "That is the visual version of the first-order **Taylor expansion** that relates a function's derivatives to its value under translation:"
        },
        {
          "start_line": 584,
          "end_line": 592,
          "markdown": "```math\nf(x-a)\n=\nf(x)\n-\na\\frac{df}{dx}\n+\nO(a^2).\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_translation_tangent_zoom_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the translation-and-tangent-zoom frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "render() sets the exact symmetry-translation-tangent-zoom output stem. Constants specify 1280 x 720, 24 fps, 360 frames. No matching validation JSON is present; attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_translation_tangent_zoom_animation.py",
          "file": "sources/scripts/generate_symmetry_translation_tangent_zoom_animation.py.txt",
          "sha256": "3ed12cd6638ecf0dbef49b37d1613cacc0f4dbf94abea8feb907f9efc83c6679",
          "bytes": 8859,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_translation_tangent_zoom_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-translation-tangent-zoom/manifest.json",
      "manifest_sha256": "af165f664df8232d33abce25b1fc9d151fcb90cca304601335df33a1207ff654",
      "frame_directory": "symmetry-translation-tangent-zoom/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-translation-tangent-zoom.mp4",
        "video_sha256": "a4db667ace47c5f9a12f280dbb6ac8ef4002e3511aedf2c5824cc67cc5fa9b40",
        "duration_seconds": 15.0,
        "fps": 24.0,
        "frame_count": 360,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "43bf4ad92cd819a3a667075cee87b8978c7a44ad77ae4ab5597a30dfc3333fd4"
          },
          {
            "file": "frame-02.jpg",
            "index": 33,
            "time_seconds": 1.375,
            "sha256": "cc0c068f95290429c01ce5d0835b18e10e8e372a4c31ee9ccf7ad222afd6d569"
          },
          {
            "file": "frame-03.jpg",
            "index": 65,
            "time_seconds": 2.708333,
            "sha256": "4aec2afef1ea02e2e308f3802caba72ba0d1821f0cf614c92b35827c2b1fd22f"
          },
          {
            "file": "frame-04.jpg",
            "index": 98,
            "time_seconds": 4.083333,
            "sha256": "916ed2d4295f25ddeacb9cc4ef4ca184e6541ea2784c8f4c225dfb539fd599d0"
          },
          {
            "file": "frame-05.jpg",
            "index": 131,
            "time_seconds": 5.458333,
            "sha256": "164e6b1d42d49a965a65e42288d093f27dd71716dca8ccd4be65e2a2145f3e07"
          },
          {
            "file": "frame-06.jpg",
            "index": 163,
            "time_seconds": 6.791667,
            "sha256": "fa2a4c870a2bd586408f5cddfb17d7ce7cf32e91437511a472b0635df7fc93aa"
          },
          {
            "file": "frame-07.jpg",
            "index": 196,
            "time_seconds": 8.166667,
            "sha256": "3ed8286c8d2868c3d7eaea0afc9b9b2d5d4102dcb05b289570b351e9a19c8cab"
          },
          {
            "file": "frame-08.jpg",
            "index": 228,
            "time_seconds": 9.5,
            "sha256": "d7f819ad789c00f8cae5a96fae30f348336717b8c99da7f19301d6b77b4e117d"
          },
          {
            "file": "frame-09.jpg",
            "index": 261,
            "time_seconds": 10.875,
            "sha256": "406aa2871cbeb8e79fed76d5083ce2707f5583ebb3bbb16aaf6a5ea516e3e9b9"
          },
          {
            "file": "frame-10.jpg",
            "index": 294,
            "time_seconds": 12.25,
            "sha256": "ab991ee1707e685756b9de717745289c13c937c39feaa69fb7a6cd9dfc636c42"
          },
          {
            "file": "frame-11.jpg",
            "index": 326,
            "time_seconds": 13.583333,
            "sha256": "ab991ee1707e685756b9de717745289c13c937c39feaa69fb7a6cd9dfc636c42"
          },
          {
            "file": "frame-12.jpg",
            "index": 359,
            "time_seconds": 14.958333,
            "sha256": "ab991ee1707e685756b9de717745289c13c937c39feaa69fb7a6cd9dfc636c42"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "1ad74fbe6593786a1f7f3ea208c578286422c6344e4f41e7720ffe2220184a8b"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-translation-tangent-zoom",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-translation-tangent-zoom",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-translation-tangent-zoom.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-translation-tangent-zoom/"
    },
    {
      "video": "content/drafts/animations/symmetry-eigenbasis-stretch.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-eigenbasis-stretch.mp4",
      "basename": "symmetry-eigenbasis-stretch.mp4",
      "slug": "symmetry-eigenbasis-stretch",
      "chapter_section": "Primer -- Eigenfunctions",
      "caption": "Eigenvectors pictured",
      "image_description": "Stretching in ordinary and eigenvector bases",
      "video_link_line": 622,
      "context_before": [
        {
          "start_line": 614,
          "end_line": 616,
          "markdown": "| $(x,y)$ basis: components mix | $(u,v)$ basis: components do not mix |\n|---|---|\n| $\\displaystyle \\begin{aligned}\\mathbf r&=\\frac12\\begin{pmatrix}s+s^{-1}&s-s^{-1}\\\\s-s^{-1}&s+s^{-1}\\end{pmatrix}\\mathbf r_{\\mathrm{in}}\\\\&=\\frac12\\left[(s+s^{-1})x+(s-s^{-1})y\\right]\\hat{\\mathbf x}\\\\&\\quad+\\frac12\\left[(s-s^{-1})x+(s+s^{-1})y\\right]\\hat{\\mathbf y}\\end{aligned}$ | $\\displaystyle \\begin{aligned}\\mathbf r&=\\begin{pmatrix}s&0\\\\0&s^{-1}\\end{pmatrix}\\mathbf r_{\\mathrm{in}}\\\\&=su\\hat{\\mathbf u}+s^{-1}v\\hat{\\mathbf v}\\end{aligned}$ |"
        },
        {
          "start_line": 618,
          "end_line": 618,
          "markdown": "This is readily understood visually:"
        }
      ],
      "context_after": [
        {
          "start_line": 626,
          "end_line": 626,
          "markdown": "Now for a bunch of terminology. The \"natural\" basis vectors are the **eigenvectors**, the values a transformation scales these by are the **eigenvalues** and the basis they form is called the **eigenbasis**. When the \"vectors\" are functions, we call them **eigenfunctions**. As the transformation matrix is diagonal in the eigenbasis, the procedure for finding an eigenbasis is typically called **diagonalization**. "
        }
      ],
      "sources": [
        "scripts/generate_symmetry_eigenbasis_stretch_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws the eigenbasis-stretch frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "render() sets the exact symmetry-eigenbasis-stretch output stem. Constants specify 1280 x 720 at 24 fps. No matching validation JSON is present; attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_eigenbasis_stretch_animation.py",
          "file": "sources/scripts/generate_symmetry_eigenbasis_stretch_animation.py.txt",
          "sha256": "290c0d3d8b0e69edb127ce8ab7568c60d3679c3eb5b2d012ceb7cdfba2c89f55",
          "bytes": 15419,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_eigenbasis_stretch_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-eigenbasis-stretch/manifest.json",
      "manifest_sha256": "c0af5cc74241811f7e227907687b8937e9f9fe2f10a70a40db5055074c6e2cb0",
      "frame_directory": "symmetry-eigenbasis-stretch/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-eigenbasis-stretch.mp4",
        "video_sha256": "627d8caf6e17df54a5c6851f9ca579314a2d120308ea56741b74c40388b9f31e",
        "duration_seconds": 14.5,
        "fps": 24.0,
        "frame_count": 348,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "8a80ef3c262fd828f08622c13e602ca7624106031e09701f5c48dd126e4fe976"
          },
          {
            "file": "frame-02.jpg",
            "index": 32,
            "time_seconds": 1.333333,
            "sha256": "281fc2c6181ae5a1f64584040cb1f4dad6b2db6ecd552f23eaa57847821f35b0"
          },
          {
            "file": "frame-03.jpg",
            "index": 63,
            "time_seconds": 2.625,
            "sha256": "00161c5dba99f4c003be03571a89665381ba933c80291e3c270e6ef8394ddcdc"
          },
          {
            "file": "frame-04.jpg",
            "index": 95,
            "time_seconds": 3.958333,
            "sha256": "bcddb27f37f1f00a1ef5be8019f9f2f937d01ee29abd286d364c24aeaa1fd2f4"
          },
          {
            "file": "frame-05.jpg",
            "index": 126,
            "time_seconds": 5.25,
            "sha256": "2cafa784d6696bda4d750f52b1593219613c85d6754b4a4fb6c80c754888606f"
          },
          {
            "file": "frame-06.jpg",
            "index": 158,
            "time_seconds": 6.583333,
            "sha256": "1236feca32121d496bba4dec3179fd062ca671db0ccedafc78fd181cedf27c4d"
          },
          {
            "file": "frame-07.jpg",
            "index": 189,
            "time_seconds": 7.875,
            "sha256": "b9f6b21ed2bad9d7d990d684820d8e8114f1d93c5ccc834c319c90a0c402aaa1"
          },
          {
            "file": "frame-08.jpg",
            "index": 221,
            "time_seconds": 9.208333,
            "sha256": "672a41d2f6472908f151b3746ef115f7399cfb6586904bdfbd9f4b1846f75432"
          },
          {
            "file": "frame-09.jpg",
            "index": 252,
            "time_seconds": 10.5,
            "sha256": "9dff20e4502fa331f2943d0480f9ca55b7d047a21701fa43df50c6827a9088f2"
          },
          {
            "file": "frame-10.jpg",
            "index": 284,
            "time_seconds": 11.833333,
            "sha256": "f5e5819d14fc6b9aeb4c4154d653fafb3087981654d54c23a0f1250a08f655e7"
          },
          {
            "file": "frame-11.jpg",
            "index": 315,
            "time_seconds": 13.125,
            "sha256": "c75c4327bf6f1da5aef16904e57d7836aa52bd777cf8aff8ca080985c2d5d53a"
          },
          {
            "file": "frame-12.jpg",
            "index": 347,
            "time_seconds": 14.458333,
            "sha256": "c75c4327bf6f1da5aef16904e57d7836aa52bd777cf8aff8ca080985c2d5d53a"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "c241624dec32cd32213c7d209ddbc2cffe114fe1d3a30c0a52d51d0adb20238f"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-eigenbasis-stretch",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-eigenbasis-stretch",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-eigenbasis-stretch.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-eigenbasis-stretch/"
    },
    {
      "video": "content/drafts/animations/symmetry-complex-plane-wave-v2.mp4",
      "original_reference": "animations/symmetry-complex-plane-wave-v2.mp4",
      "basename": "symmetry-complex-plane-wave-v2.mp4",
      "slug": "symmetry-complex-plane-wave-v2",
      "chapter_section": "Primer - Complex exponentials and Wave Packets",
      "caption": "The complex value rotates while its length stays fixed.",
      "image_description": "A complex plane wave, its two components, and the rotating value at a fixed position",
      "video_link_line": 781,
      "context_before": [
        {
          "start_line": 775,
          "end_line": 775,
          "markdown": "Here $A$ is the wave's amplitude, $\\lambda$ is its wavelength, $k=2\\pi/\\lambda$ is its **wave number**, and $\\omega$ sets how quickly the phase changes with time."
        },
        {
          "start_line": 777,
          "end_line": 777,
          "markdown": "In the animation, position runs along the helix. The other two directions show the real and imaginary parts of the complex value at each position. As time advances, these values rotate and the wave pattern travels along $x$."
        }
      ],
      "context_after": [
        {
          "start_line": 785,
          "end_line": 785,
          "markdown": "The two components are $A\\cos\\theta$ and $A\\sin\\theta$. Their values oscillate, while the length of the complex value stays fixed:"
        },
        {
          "start_line": 787,
          "end_line": 789,
          "markdown": "```math\n|z|=\\sqrt{A^2\\cos^2\\theta+A^2\\sin^2\\theta}=A.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_complex_plane_wave_v2.py"
      ],
      "generation_note": "Main script --render draws the complex plane-wave frames and encodes them directly. --check and --encoded-check produce the companion mathematical and decoded-video reports. It has no local Python imports or input movie.",
      "evidence": "The script's NAME and output match the linked movie. Matching validation and encoded-validation JSON record 1440 x 900, 30 fps, 360 frames, 12 s. The reports contain no movie hash, so this is source/output-name attribution rather than a hash-attested build.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_complex_plane_wave_v2.py",
          "file": "sources/scripts/generate_symmetry_complex_plane_wave_v2.py.txt",
          "sha256": "6dc77c751f396bdc8d8a8d78ff7606b9331a5e0a31dc0aa9d2b556a0bbee6fd8",
          "bytes": 17921,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_complex_plane_wave_v2.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-complex-plane-wave-v2-validation.json",
          "file": "symmetry-complex-plane-wave-v2/checks/symmetry-complex-plane-wave-v2-validation.json",
          "sha256": "482c1bdae6e380e4796007acf9c50dbcb46ab0540efa25539400407f25e65cb7",
          "bytes": 1303
        },
        {
          "path": "content/drafts/animations/symmetry-complex-plane-wave-v2-encoded-validation.json",
          "file": "symmetry-complex-plane-wave-v2/checks/symmetry-complex-plane-wave-v2-encoded-validation.json",
          "sha256": "aad970dd9e74c8f246b365431c9db9c8ab767f338f43a90e1983392284911b6a",
          "bytes": 503
        }
      ],
      "manifest": "symmetry-complex-plane-wave-v2/manifest.json",
      "manifest_sha256": "ca3c574cde00b36716ba1c5af7298c17c37695406426d357d9c4356a735c5d64",
      "frame_directory": "symmetry-complex-plane-wave-v2/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-complex-plane-wave-v2.mp4",
        "video_sha256": "e407d6199cc5539ba3a33469fd1a417d21023bef7461f1acad2ecd11fa903892",
        "duration_seconds": 12.0,
        "fps": 30.0,
        "frame_count": 360,
        "width": 1440,
        "height": 900,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "7c70c331e0a56ce9d678463ad06af8eac6ac1026b632b230dff20ed375e0fa49"
          },
          {
            "file": "frame-02.jpg",
            "index": 33,
            "time_seconds": 1.1,
            "sha256": "0fc832dca21a630310d083fa64b00189050042dfacca138eb0239548547bf1f5"
          },
          {
            "file": "frame-03.jpg",
            "index": 65,
            "time_seconds": 2.166667,
            "sha256": "096d4b8151674206fe5c5d941465f0890edc2a11b5f44a94c6862ae73f264638"
          },
          {
            "file": "frame-04.jpg",
            "index": 98,
            "time_seconds": 3.266667,
            "sha256": "b180baa3508a97d31225b3544f5552fd96db7e37a75fbfc883fdfd5c1448c0f6"
          },
          {
            "file": "frame-05.jpg",
            "index": 131,
            "time_seconds": 4.366667,
            "sha256": "ee7a17263d08f14a8eadd6945c46cd140c0b228faa9f0b6c147d4f1c2230ef81"
          },
          {
            "file": "frame-06.jpg",
            "index": 163,
            "time_seconds": 5.433333,
            "sha256": "9dc23f74a45c727cd05643564e4dd138723bfdbe54c61dd68e0ca520edbf66dd"
          },
          {
            "file": "frame-07.jpg",
            "index": 196,
            "time_seconds": 6.533333,
            "sha256": "c083abc12ab894fcb847dc649d47cfe9e0a08561efa834bdc5eb4083bd0f5fce"
          },
          {
            "file": "frame-08.jpg",
            "index": 228,
            "time_seconds": 7.6,
            "sha256": "21298c1bd7ce5e37a8d148035e6e6fdb9967f9cdac9d01d039ebcfd020004abc"
          },
          {
            "file": "frame-09.jpg",
            "index": 261,
            "time_seconds": 8.7,
            "sha256": "1164204aef73fdda80ffa168f7cad686614fb422b8e035fd77d0281ebee9d1ce"
          },
          {
            "file": "frame-10.jpg",
            "index": 294,
            "time_seconds": 9.8,
            "sha256": "c0ba7e7362a715a409ee07dd730a1a1bb7f3f3ad5da00a70ffc65f16b17e13b0"
          },
          {
            "file": "frame-11.jpg",
            "index": 326,
            "time_seconds": 10.866667,
            "sha256": "56b6fdbbcc1d15dd69362db7b0e60f7222d13eb14e78db60655d942804b5b185"
          },
          {
            "file": "frame-12.jpg",
            "index": 359,
            "time_seconds": 11.966667,
            "sha256": "ff86ba2b7c616dfeb19b9f5005192e917b819b580b22e28dac07ab05e958dfe6"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "22c570490f6b94b2ec2d32d8752eac963ae1af3c6e2645921fbaf3d934cbd970"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-complex-plane-wave-v2",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-complex-plane-wave-v2",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-complex-plane-wave-v2.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-complex-plane-wave-v2/"
    },
    {
      "video": "content/drafts/animations/symmetry-fourier-three-tone-packet.mp4",
      "original_reference": "animations/symmetry-fourier-three-tone-packet.mp4",
      "basename": "symmetry-fourier-three-tone-packet.mp4",
      "slug": "symmetry-fourier-three-tone-packet",
      "chapter_section": "The Fourier Structure of Waves",
      "caption": null,
      "image_description": "Three-tone chord packet and its Fourier decomposition",
      "video_link_line": 804,
      "context_before": [
        {
          "start_line": 800,
          "end_line": 800,
          "markdown": "If you strike a chord on a piano, some complicated function of time describes how the sound pressure reaches your ear. It starts soft, gets louder, softens again. It has discernible main tones, but also a clutter of overtones that comprise the timbre of the piano. While you hear a clear tonal structure, a plot of the sound pressure level over time reaching your ear would completely obscure that structure. "
        }
      ],
      "context_after": [
        {
          "start_line": 806,
          "end_line": 806,
          "markdown": "But we know something about this random-seeming function reaching your ear. We know it is comprised of mostly 3 pitches, and some overtones. If we plot the sound pressure level as a function of these pitches rather than as a function of time, the structure of our plot clearly reveals what we hear naturally with our ear. What we call a \"pitch\" is the frequency of a pure **mode**. The pattern of sound pressure over time is the sum, or \"composition,\" of these modes. We can transform back and forth between the wave number and position representations. Typically we label the wave number representation with a tilde:"
        },
        {
          "start_line": 808,
          "end_line": 810,
          "markdown": "```math\n\\psi(x)\\;\\longleftrightarrow\\;\\tilde{\\psi}(k)\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_fourier_chord_packet_animation.py"
      ],
      "generation_note": "Run the main script without arguments. Its Fourier-chord renderer draws the three-tone packet frames with Pillow and encodes them with FFmpeg. It has no local Python imports or input movie.",
      "evidence": "Despite the generator filename, render() explicitly sets the output stem to symmetry-fourier-three-tone-packet. Constants specify 1280 x 720, 24 fps, 192 frames. No matching validation JSON is present; attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_fourier_chord_packet_animation.py",
          "file": "sources/scripts/generate_symmetry_fourier_chord_packet_animation.py.txt",
          "sha256": "3b393e6935e8e605d02a727f38f1c9953f14ee7b0bcbe34ce85aac42086078b6",
          "bytes": 16392,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_fourier_chord_packet_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-fourier-three-tone-packet/manifest.json",
      "manifest_sha256": "f08ab67afa24003d08126c7e5c95a819ac3126ddc80571f5a387ba977b707c1f",
      "frame_directory": "symmetry-fourier-three-tone-packet/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-fourier-three-tone-packet.mp4",
        "video_sha256": "09e84cada72761b75dfe9757ad61add85c19efcc3223b9be4f20c62a74c026de",
        "duration_seconds": 8.0,
        "fps": 24.0,
        "frame_count": 192,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "328296d02328bb1834377a4822b1c44d3d60623b0823b3395c9f0e6ed9fb5537"
          },
          {
            "file": "frame-02.jpg",
            "index": 17,
            "time_seconds": 0.708333,
            "sha256": "3a5e5631b07882be8553c357ef2fc43e2a5d0cdd023dabf4cd952e9591d0bf8f"
          },
          {
            "file": "frame-03.jpg",
            "index": 35,
            "time_seconds": 1.458333,
            "sha256": "34fe8cb33932e23dfe1bf1a080f00018717400400f9fa797c2b3804075b51958"
          },
          {
            "file": "frame-04.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "b0db1c6f65bb69092e5e3cf8033bbccc1664a3e35259e7029d74ebae153fd08e"
          },
          {
            "file": "frame-05.jpg",
            "index": 69,
            "time_seconds": 2.875,
            "sha256": "4fd08b400330136c0a4d771d143ba1036c0cdb1c3aab1cb54b8f5a5e7c314de6"
          },
          {
            "file": "frame-06.jpg",
            "index": 87,
            "time_seconds": 3.625,
            "sha256": "37342f7be9872730ed11c0c0fc5605a88b9f6799878ebd8950510956733f9161"
          },
          {
            "file": "frame-07.jpg",
            "index": 104,
            "time_seconds": 4.333333,
            "sha256": "6813d547fcbebb374bd297531feafac2c346be48fde3a3c18be6adfd1a8d9c0a"
          },
          {
            "file": "frame-08.jpg",
            "index": 122,
            "time_seconds": 5.083333,
            "sha256": "d847c1d29ce2419af41e6d69cff115cc24e45977c29cb45f081b7a50c3c1e7aa"
          },
          {
            "file": "frame-09.jpg",
            "index": 139,
            "time_seconds": 5.791667,
            "sha256": "08d4a45f8462d0c8b975d07afa392a3e359fc897538a3ae7b2e0fdec6b6c25c7"
          },
          {
            "file": "frame-10.jpg",
            "index": 156,
            "time_seconds": 6.5,
            "sha256": "e81624ce6e2f188902edabc3d78539cdd0c995748e23a4273d85b1c74749eed4"
          },
          {
            "file": "frame-11.jpg",
            "index": 174,
            "time_seconds": 7.25,
            "sha256": "8af7c4bb186cdd745e7403a1098abcfd48f2f4f20755ccb68b192c133a2903d9"
          },
          {
            "file": "frame-12.jpg",
            "index": 191,
            "time_seconds": 7.958333,
            "sha256": "f46825af9ae0860bd31191ae720d0bec489a31c849c9a1175fac64f3ba7c4dba"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "89d038696f1b8cfad6a96fde2cf9f9b22322b05540352f422cb7b2e9a55b1ca4"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-fourier-three-tone-packet",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-fourier-three-tone-packet",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-fourier-three-tone-packet.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-fourier-three-tone-packet/"
    },
    {
      "video": "content/drafts/animations/symmetry-ccr-x-k-translations-symmetric.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-x-k-translations-symmetric.mp4",
      "basename": "symmetry-ccr-x-k-translations-symmetric.mp4",
      "slug": "symmetry-ccr-x-k-translations-symmetric",
      "chapter_section": "The Heisenberg Symmetry Group",
      "caption": "Wave Packet translated in position and momentum space",
      "image_description": "Separate translations in position and wave number, each shown in its own representation",
      "video_link_line": 845,
      "context_before": [
        {
          "start_line": 839,
          "end_line": 839,
          "markdown": "We can understand Fourier analysis in terms of the symmetry group that acts on wave functions, the Heisenberg group. This group's actions preserve the overlaps between states, and therefore their distinguishability. They do not in general preserve the behavior of those states as they evolve. In this sense, they are symmetries of state space, whether or not they are also dynamical symmetries of a particular system. This group not only underlies Fourier analysis, but in defining similarity and distinguishability of wave functions, supplies an essential ingredient for a logically viable notion of state."
        },
        {
          "start_line": 841,
          "end_line": 841,
          "markdown": "We can translate a wave function either in $x$-space or in $k$-space. While shifting the wave number isn't a translation in the familiar physical space we live in, from a mathematical perspective, $k$-space is the dual, or equivalent up to role reversal, of $x$-space. "
        }
      ],
      "context_after": [
        {
          "start_line": 849,
          "end_line": 849,
          "markdown": "The overlap between two complex wave functions is their **inner product**:"
        },
        {
          "start_line": 851,
          "end_line": 855,
          "markdown": "```math\n\\langle\\psi_1,\\psi_2\\rangle\n=\n\\int_{-\\infty}^{\\infty}\\psi_1^*(x)\\psi_2(x)\\,dx.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_ccr_x_k_translations_symmetric.py"
      ],
      "generation_note": "Run the main script without --stills-only. It validates the Gaussian Fourier pair, draws both panels with Pillow, and pipes 510 RGB frames to FFmpeg. No local Python imports or input movie are used.",
      "evidence": "The script's NAME exactly matches the linked movie; render() writes that MP4 and decodes a sample at 9.5 s. Constants specify 1440 x 810, 30 fps, 17 s. No matching validation JSON is present, so this is a source/output-name attribution rather than a hash-attested build.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_ccr_x_k_translations_symmetric.py",
          "file": "sources/scripts/generate_symmetry_ccr_x_k_translations_symmetric.py.txt",
          "sha256": "3962202e5701486a292cfe2d80f2de89da1197fd8980afce44d077e9032cc4ba",
          "bytes": 11551,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_ccr_x_k_translations_symmetric.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-ccr-x-k-translations-symmetric/manifest.json",
      "manifest_sha256": "88856e2b0ab42f512dc2f6c091e98daf0e137b0ed89384c96418c0b3ab18a3b4",
      "frame_directory": "symmetry-ccr-x-k-translations-symmetric/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-ccr-x-k-translations-symmetric.mp4",
        "video_sha256": "117304347b7c29660a1f3a461d385aaed8b99c250a0ddffe5d77a9ac6a3d203f",
        "duration_seconds": 17.0,
        "fps": 30.0,
        "frame_count": 510,
        "width": 1440,
        "height": 810,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "0266e46e399c1339280e07ac4d5646266d88f29499cb5fae34591608c4d1bf13"
          },
          {
            "file": "frame-02.jpg",
            "index": 46,
            "time_seconds": 1.533333,
            "sha256": "92b21d65c64c4f164666722c2ac3449a5f8e3a668f0f977bbfbf3f3a7af4cfd0"
          },
          {
            "file": "frame-03.jpg",
            "index": 93,
            "time_seconds": 3.1,
            "sha256": "49b756eae05c4e17c04fa0578c207eb499d3e4740a84ecd62bc87fede6c7a698"
          },
          {
            "file": "frame-04.jpg",
            "index": 139,
            "time_seconds": 4.633333,
            "sha256": "7390801b4125573a0c178e2abc13168be2f8da2499052b910ff8eff91d051e0f"
          },
          {
            "file": "frame-05.jpg",
            "index": 185,
            "time_seconds": 6.166667,
            "sha256": "bdca78705752b362f454c6db3f60638acd6b3e8d089fe3364362fc6d7850e99a"
          },
          {
            "file": "frame-06.jpg",
            "index": 231,
            "time_seconds": 7.7,
            "sha256": "880edcbba7fbbb692a0686662be2a75076cddf0366689a66856241048675b5d5"
          },
          {
            "file": "frame-07.jpg",
            "index": 278,
            "time_seconds": 9.266667,
            "sha256": "c56afe603ad34bd45bc639378fa8b865bb0efa6020d1da8fa80830328066c1eb"
          },
          {
            "file": "frame-08.jpg",
            "index": 324,
            "time_seconds": 10.8,
            "sha256": "2a1e5aca58ab21113bd2ed079924109c4ad4e74e5f68bf2ab1684b7d22d3b4eb"
          },
          {
            "file": "frame-09.jpg",
            "index": 370,
            "time_seconds": 12.333333,
            "sha256": "ddf81c1b473203b267cf9daa2ce912057a1ddd17e0d88bb2eee577c7c12f8151"
          },
          {
            "file": "frame-10.jpg",
            "index": 416,
            "time_seconds": 13.866667,
            "sha256": "b58d3c1a43d1064cac239d4c33d6d3d009eb1e58080e66a3d2ff7c53b5023f70"
          },
          {
            "file": "frame-11.jpg",
            "index": 463,
            "time_seconds": 15.433333,
            "sha256": "6ec745de13c40414a917a09166383518b9e1d12eb9dea991c2b7a38e6f1c45d6"
          },
          {
            "file": "frame-12.jpg",
            "index": 509,
            "time_seconds": 16.966667,
            "sha256": "de45b998d7227ed576b0ff221364bebe77e0e9a1f16b368959e1808366ac40aa"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "edfaa0d501f86fda1ff79ee8d15f80e9109ca59a99dc3176c3ee32e6025bdcee"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-ccr-x-k-translations-symmetric",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-ccr-x-k-translations-symmetric",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-x-k-translations-symmetric.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-ccr-x-k-translations-symmetric/"
    },
    {
      "video": "content/drafts/animations/symmetry-ccr-unitarity.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-unitarity.mp4",
      "basename": "symmetry-ccr-unitarity.mp4",
      "slug": "symmetry-ccr-unitarity",
      "chapter_section": "The Heisenberg Symmetry Group",
      "caption": "Unitarity of position and wave-number translations",
      "image_description": "Four panes show two wave functions translating in x or k while their overlap is preserved, with schematic state-space projections for a real, positive overlap",
      "video_link_line": 887,
      "context_before": [
        {
          "start_line": 869,
          "end_line": 869,
          "markdown": "This invariance, as we've seen, is called unitarity. It preserves the distinguishability of states under symmetry transformations and makes those transformations reversible. When time evolution is itself unitary, the evolution of the state is deterministic and reversible. Translations in position and wave number preserve the inner product:"
        },
        {
          "start_line": 871,
          "end_line": 883,
          "markdown": "```math\n\\begin{gathered}\n\\text{Translation by }a\\text{ in }x\\\\[0.5em]\n\\langle T_x(a)\\psi,T_x(a)\\chi\\rangle\\\\\n=\\langle\\psi,\\chi\\rangle\n\\end{gathered}\n\\qquad\n\\begin{gathered}\n\\text{Translation by }b\\text{ in }k\\\\[0.5em]\n\\langle T_k(b)\\psi,T_k(b)\\chi\\rangle\\\\\n=\\langle\\psi,\\chi\\rangle\n\\end{gathered}\n```"
        }
      ],
      "context_after": [
        {
          "start_line": 891,
          "end_line": 891,
          "markdown": "In addition to position and wave number, waves have a third independent way of changing. A wave's **phase**, $\\phi$, refers to where it is in its cyclic pattern. For example, a phase shift of $2\\pi$, or one full \"cycle,\" returns the wave to its exact initial state. We need to be a bit careful here. For a pure mode, shifting its position is indistinguishable from shifting its phase, somewhat in the way the turning of a barbershop sign appears as though its stripes are moving up and down. We might, then, be tempted to think there is no difference between position and phase shifts. But the single mode is an idealization. In the general case, in which the wave function is a packet composed of modes, position translation shifts the entire function. Phase translation shifts each mode by the same fraction of its cycle, changing the function while leaving its magnitude envelope unchanged."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_ccr_unitarity.py"
      ],
      "generation_note": "Main script --render writes the MP4; --check and --encoded-check write mathematical and decoded-video validation. The renderer is self-contained apart from external NumPy/Pillow/imageio-ffmpeg libraries.",
      "evidence": "NAME and output path match. content/drafts/animations/symmetry-ccr-unitarity-validation.json records 1440 x 1040, 30 fps, 510 frames, 17 s; the matching encoded-validation.json records the same geometry and frame count and compares decoded frames with source renders.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_ccr_unitarity.py",
          "file": "sources/scripts/generate_symmetry_ccr_unitarity.py.txt",
          "sha256": "4fca981a5cf3731c12d09aaf28e4c5bbb8aafc528af6fd00a43f42f5eaddef8d",
          "bytes": 17163,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_ccr_unitarity.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-ccr-unitarity-validation.json",
          "file": "symmetry-ccr-unitarity/checks/symmetry-ccr-unitarity-validation.json",
          "sha256": "aaa39897c718ba34f0d3768436f21949a53ee3b77dfa0a30b01f4c99ecf463cd",
          "bytes": 850
        },
        {
          "path": "content/drafts/animations/symmetry-ccr-unitarity-encoded-validation.json",
          "file": "symmetry-ccr-unitarity/checks/symmetry-ccr-unitarity-encoded-validation.json",
          "sha256": "5e7d6deb56ef5d4bbb7920cabf4386df820e3d62c26ba4c784752a9d7bbcb88e",
          "bytes": 146
        }
      ],
      "manifest": "symmetry-ccr-unitarity/manifest.json",
      "manifest_sha256": "61e5eb2b68de2cd84828734755df8fcf9bc68ae0a379702d5d223e9a61f5a777",
      "frame_directory": "symmetry-ccr-unitarity/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-ccr-unitarity.mp4",
        "video_sha256": "b101e244c9651fa1e0979a1ac7450bd9234d09251a78efd750a888f8bfca636d",
        "duration_seconds": 17.0,
        "fps": 30.0,
        "frame_count": 510,
        "width": 1440,
        "height": 1040,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "dc10a4833bff278a84c437850c229fcae65e5a4bf7c583543dfbad28d01b4301"
          },
          {
            "file": "frame-02.jpg",
            "index": 46,
            "time_seconds": 1.533333,
            "sha256": "23dff1cb24e2968a8d098c3744ea7337e683588fa5e770d10d1345c013229688"
          },
          {
            "file": "frame-03.jpg",
            "index": 93,
            "time_seconds": 3.1,
            "sha256": "2fe28ff2df18e5ba55f9678ad5cbe9f4fc76c42904208caa85fbb3e59a0e586a"
          },
          {
            "file": "frame-04.jpg",
            "index": 139,
            "time_seconds": 4.633333,
            "sha256": "4a48096e560fbb0bfa3190a33d4ef5ed64b837681340a9bf57c60db044b2651d"
          },
          {
            "file": "frame-05.jpg",
            "index": 185,
            "time_seconds": 6.166667,
            "sha256": "4dd80682b984982ac7f87babb5462964c63d8e60fc2dc6f5257a3f31d6925829"
          },
          {
            "file": "frame-06.jpg",
            "index": 231,
            "time_seconds": 7.7,
            "sha256": "673031428370b494c365cd4a91847984ec845cad34bc89756787a51cf1340f4d"
          },
          {
            "file": "frame-07.jpg",
            "index": 278,
            "time_seconds": 9.266667,
            "sha256": "d9f7a47974d9dfd66d84ff894d5837d1418e2c820a0923b3d09cd32242bf69c6"
          },
          {
            "file": "frame-08.jpg",
            "index": 324,
            "time_seconds": 10.8,
            "sha256": "cf5fb53bb7a7f9810dd9eba9e9443fdd3ed186848125a46819cd59da3d08a1a2"
          },
          {
            "file": "frame-09.jpg",
            "index": 370,
            "time_seconds": 12.333333,
            "sha256": "b33adac356914136000a51fb2b3241b460f23f750b92f744ea1585ade7e5f646"
          },
          {
            "file": "frame-10.jpg",
            "index": 416,
            "time_seconds": 13.866667,
            "sha256": "f0a915fe14eb9e5b157ecfd095e4fce0445b14f0af79b4267c8f61e4ce9bff64"
          },
          {
            "file": "frame-11.jpg",
            "index": 463,
            "time_seconds": 15.433333,
            "sha256": "f45143517b497989450071023d3b94dd270fb43999633ff540f53d637815b27d"
          },
          {
            "file": "frame-12.jpg",
            "index": 509,
            "time_seconds": 16.966667,
            "sha256": "a1aab93acd1adb45bbf94205c71fa331fb9924f6f6ef8d5b1e42d1033148a547"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "074757e417bf5b2cb7bc423345decbb9afeb31c255f8e9887b1e50e82eb75591"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-ccr-unitarity",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-ccr-unitarity",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-unitarity.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-ccr-unitarity/"
    },
    {
      "video": "content/drafts/animations/symmetry-complex-phase-modes.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-complex-phase-modes.mp4",
      "basename": "symmetry-complex-phase-modes.mp4",
      "slug": "symmetry-complex-phase-modes",
      "chapter_section": "The Heisenberg Symmetry Group",
      "caption": "Visualizing Phase Change",
      "image_description": "Nine complex modes and their exact sum rotate through five phase turns while their magnitude envelopes remain fixed",
      "video_link_line": 895,
      "context_before": [
        {
          "start_line": 891,
          "end_line": 891,
          "markdown": "In addition to position and wave number, waves have a third independent way of changing. A wave's **phase**, $\\phi$, refers to where it is in its cyclic pattern. For example, a phase shift of $2\\pi$, or one full \"cycle,\" returns the wave to its exact initial state. We need to be a bit careful here. For a pure mode, shifting its position is indistinguishable from shifting its phase, somewhat in the way the turning of a barbershop sign appears as though its stripes are moving up and down. We might, then, be tempted to think there is no difference between position and phase shifts. But the single mode is an idealization. In the general case, in which the wave function is a packet composed of modes, position translation shifts the entire function. Phase translation shifts each mode by the same fraction of its cycle, changing the function while leaving its magnitude envelope unchanged."
        }
      ],
      "context_after": [
        {
          "start_line": 899,
          "end_line": 899,
          "markdown": "We can also discover and define phase directly from our symmetry group's commutation relations, which gives us a useful algebraic packaging of the group structure. Let's ask the question:"
        },
        {
          "start_line": 901,
          "end_line": 903,
          "markdown": "```math\n[\\hat X, \\hat K] = \\; ?\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_complex_phase_modes.py",
        "scripts/generate_symmetry_packet_phase_modes.py"
      ],
      "generation_note": "Main script --render supplies the complex-mode frame renderer and uses drawing, numerical, and encoding helpers imported from generate_symmetry_packet_phase_modes. The imported script is a code dependency, not an input movie. --check and --encoded-check produce the companion checks.",
      "evidence": "Exact NAME match. The matching validation JSON records nine wave numbers 11 through 19, five phase turns, 540 frames at 30 fps, 1440 x 1080, 18 s; encoded-validation.json confirms that frame count and compares decoded frames with this renderer.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_complex_phase_modes.py",
          "file": "sources/scripts/generate_symmetry_complex_phase_modes.py.txt",
          "sha256": "ca642fc9b34286da0cb58cfd6ea097abd347b9df6b00a7f58ca0f76ae311d39d",
          "bytes": 14111,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_complex_phase_modes.py"
        },
        {
          "path": "scripts/generate_symmetry_packet_phase_modes.py",
          "file": "sources/scripts/generate_symmetry_packet_phase_modes.py.txt",
          "sha256": "47fdf5353988db4807c5e9a21545b3f5dc44b097d7a5b077eec2889333f57edb",
          "bytes": 15777,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_packet_phase_modes.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-complex-phase-modes-validation.json",
          "file": "symmetry-complex-phase-modes/checks/symmetry-complex-phase-modes-validation.json",
          "sha256": "296253a9e08d9b2e9c68c58f928549bb81f4ca8e765607cae326f4e0914a0fd6",
          "bytes": 1467
        },
        {
          "path": "content/drafts/animations/symmetry-complex-phase-modes-encoded-validation.json",
          "file": "symmetry-complex-phase-modes/checks/symmetry-complex-phase-modes-encoded-validation.json",
          "sha256": "9bb4b715f3def3e25ea8e8e287a778d7e02fcba31fbfdac60eb7b84ce719683e",
          "bytes": 145
        }
      ],
      "manifest": "symmetry-complex-phase-modes/manifest.json",
      "manifest_sha256": "e94ac6ec511662b680f57b04d691087ac8d17dc896168bdf1aa3948a95e2fedb",
      "frame_directory": "symmetry-complex-phase-modes/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-complex-phase-modes.mp4",
        "video_sha256": "157579b5ba38bda8a03bce2d88586dd45f37ffb060cf936cedb50e029738ca93",
        "duration_seconds": 18.0,
        "fps": 30.0,
        "frame_count": 540,
        "width": 1440,
        "height": 1080,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "79ac9046b3f5c77409ddba68707a8c6130a9d80d71486de6c2fd38cc7f32746b"
          },
          {
            "file": "frame-02.jpg",
            "index": 49,
            "time_seconds": 1.633333,
            "sha256": "1b951a01dd9915ca34c827c6d8face8d980f25d72f103e937a07bbbd7e541304"
          },
          {
            "file": "frame-03.jpg",
            "index": 98,
            "time_seconds": 3.266667,
            "sha256": "73cb00148206fad0ab13785082573b37e854adc6226dbd29934230ccfab21e8e"
          },
          {
            "file": "frame-04.jpg",
            "index": 147,
            "time_seconds": 4.9,
            "sha256": "9db4698c9ed1c029cfcb1be41366f8b399f9fae66a42f25db180be6447520a59"
          },
          {
            "file": "frame-05.jpg",
            "index": 196,
            "time_seconds": 6.533333,
            "sha256": "5af7c652e185079fb036558db680d0ac46ca641a482c1ef797859579eff7d103"
          },
          {
            "file": "frame-06.jpg",
            "index": 245,
            "time_seconds": 8.166667,
            "sha256": "477123c918556b51894df2a5b2279fb13b894e35d5a9187d4fb53c1a1cdbff9b"
          },
          {
            "file": "frame-07.jpg",
            "index": 294,
            "time_seconds": 9.8,
            "sha256": "3598249f97ae2fd3dc6ea485467baa16dd82c8ba4fc1bcf7ad906feeca4da9b9"
          },
          {
            "file": "frame-08.jpg",
            "index": 343,
            "time_seconds": 11.433333,
            "sha256": "d966c36e5d64aa57dc0acd853f2ce56e814f9cdd1cdaf3f9209aec3eb7bd67da"
          },
          {
            "file": "frame-09.jpg",
            "index": 392,
            "time_seconds": 13.066667,
            "sha256": "75616b75412286887e758695ad08af17b2bfb82e504e5ede266898dd92ec0171"
          },
          {
            "file": "frame-10.jpg",
            "index": 441,
            "time_seconds": 14.7,
            "sha256": "f1b479d092419d5991d63a37258b2c1e4fc65d47bab5062f5dc2662edf4a07c9"
          },
          {
            "file": "frame-11.jpg",
            "index": 490,
            "time_seconds": 16.333333,
            "sha256": "ff68407eec0c1bc6be1cc8207f93979eddaee3c60f1c45bdc01639bc53cc8a8a"
          },
          {
            "file": "frame-12.jpg",
            "index": 539,
            "time_seconds": 17.966667,
            "sha256": "e957e45bad422b686fcc48c206835c601814c8e27a51727960b1a81ebabb0545"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "977c83bfed4e6a9766b9847b60a3e90b13b9605d5bf226ad392cd900cf0119e8"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-complex-phase-modes",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-complex-phase-modes",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-complex-phase-modes.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-complex-phase-modes/"
    },
    {
      "video": "content/drafts/animations/symmetry-ccr-loop-complex.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-ccr-loop-complex.mp4",
      "basename": "symmetry-ccr-loop-complex.mp4",
      "slug": "symmetry-ccr-loop-complex",
      "chapter_section": "The Heisenberg Symmetry Group",
      "caption": "Phase as the $[\\hat X,\\hat K]$ commutator",
      "image_description": "A complex spiral follows an x–k loop and returns to its original magnitude envelope with a quarter-turn of phase remaining",
      "video_link_line": 1000,
      "context_before": [
        {
          "start_line": 986,
          "end_line": 990,
          "markdown": "```math\n\\langle T_k(b)\\psi,T_k(b)\\chi\\rangle\n=\n\\langle\\psi,\\chi\\rangle.\n```"
        },
        {
          "start_line": 992,
          "end_line": 996,
          "markdown": "```math\n\\langle T_\\phi(\\beta)\\psi,T_\\phi(\\beta)\\chi\\rangle\n=\n\\langle\\psi,\\chi\\rangle.\n```"
        }
      ],
      "context_after": [
        {
          "start_line": 1004,
          "end_line": 1004,
          "markdown": "The infinitesimal closed $x$-$k$ loop is generated by $[\\hat X,\\hat K]$. Because the finite loop leaves a phase translation, and phase translations are generated by $iI$, we have:"
        },
        {
          "start_line": 1006,
          "end_line": 1010,
          "markdown": "```math\n[\\hat X,\\hat K]\n=\niI.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_ccr_loop_complex.py",
        "scripts/generate_symmetry_packet_phase_modes.py"
      ],
      "generation_note": "Main script --render draws the ordered +k,+x,-k,-x loop and uses the imported packet-phase script for shared artwork, math, and encoding libraries. --check and --encoded-check produce companion checks; there is no input movie.",
      "evidence": "Exact NAME match. Matching validation records the -pi/2 residual phase, 480 frames, 30 fps, 1440 x 900, 16 s, and final hold after 13 s. Matching encoded-validation.json confirms the frame count and renderer comparisons.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_ccr_loop_complex.py",
          "file": "sources/scripts/generate_symmetry_ccr_loop_complex.py.txt",
          "sha256": "2b515b256661caab4a377b29bdbc4d0e384d054039842e8b4b05a432a6947e27",
          "bytes": 14514,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_ccr_loop_complex.py"
        },
        {
          "path": "scripts/generate_symmetry_packet_phase_modes.py",
          "file": "sources/scripts/generate_symmetry_packet_phase_modes.py.txt",
          "sha256": "47fdf5353988db4807c5e9a21545b3f5dc44b097d7a5b077eec2889333f57edb",
          "bytes": 15777,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_packet_phase_modes.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-ccr-loop-complex-validation.json",
          "file": "symmetry-ccr-loop-complex/checks/symmetry-ccr-loop-complex-validation.json",
          "sha256": "5a2fe33023d8e94ff5b8b4e26893c10a71542b29bfbf7932add277e34c6d5185",
          "bytes": 696
        },
        {
          "path": "content/drafts/animations/symmetry-ccr-loop-complex-encoded-validation.json",
          "file": "symmetry-ccr-loop-complex/checks/symmetry-ccr-loop-complex-encoded-validation.json",
          "sha256": "73753f668bb54cbf3a14e22838c462e0d75a134188e956a59b8f203d8c6ce7eb",
          "bytes": 145
        }
      ],
      "manifest": "symmetry-ccr-loop-complex/manifest.json",
      "manifest_sha256": "cab7d3bd127b5328614f037cc0604f4624ef7b932647cf6ed9e13279abb2ba6e",
      "frame_directory": "symmetry-ccr-loop-complex/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-ccr-loop-complex.mp4",
        "video_sha256": "4786dad4ba257da869a943a9098907eb5375beed47e425551094e870bdcf7f70",
        "duration_seconds": 16.0,
        "fps": 30.0,
        "frame_count": 480,
        "width": 1440,
        "height": 900,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "14d8b86350821fd98d4fdd20a5edcc3da82540c8f5eb3a9f4ca5278a1ea9465c"
          },
          {
            "file": "frame-02.jpg",
            "index": 44,
            "time_seconds": 1.466667,
            "sha256": "881d065240b68ff82caa1589de0045ac680927fdd1c9f80cd4178d33c0736350"
          },
          {
            "file": "frame-03.jpg",
            "index": 87,
            "time_seconds": 2.9,
            "sha256": "f571cbed8f9d3a9f706064497d519524cfc87bbafab2717cd1e9d8e40a1691ae"
          },
          {
            "file": "frame-04.jpg",
            "index": 131,
            "time_seconds": 4.366667,
            "sha256": "a44e2a615b4d50cae6a388bd6daebcd42d7a25170694b9f9cef83ffdae7f7345"
          },
          {
            "file": "frame-05.jpg",
            "index": 174,
            "time_seconds": 5.8,
            "sha256": "02b63002c0259927e2ed10576914cf447a17ef84dc0e12aa2fa5f5df183cf563"
          },
          {
            "file": "frame-06.jpg",
            "index": 218,
            "time_seconds": 7.266667,
            "sha256": "d3d0bc47719ec3c26e4ded7312965dbe0119ae8c3e570be939542ca32b7882f8"
          },
          {
            "file": "frame-07.jpg",
            "index": 261,
            "time_seconds": 8.7,
            "sha256": "b4318bc328abb5faf35659b6e8ed47c9736a3e5e952af2ec8d073ece79dfcc55"
          },
          {
            "file": "frame-08.jpg",
            "index": 305,
            "time_seconds": 10.166667,
            "sha256": "ec55b1e052447862b5655920bdd7cab0194ea94198ef30ed8b8a0b0be3aaa5f1"
          },
          {
            "file": "frame-09.jpg",
            "index": 348,
            "time_seconds": 11.6,
            "sha256": "5cb44a22159ccea1fddaeeebaef7f93687d726fdac137408b2734e395da65d03"
          },
          {
            "file": "frame-10.jpg",
            "index": 392,
            "time_seconds": 13.066667,
            "sha256": "48862fd516afdcb589a928cb96769fa7368d01cca9dd2fa84fb738b77f771245"
          },
          {
            "file": "frame-11.jpg",
            "index": 435,
            "time_seconds": 14.5,
            "sha256": "1d9ae3db8f28f32e6c90b848fc976431dbc29d2e888a531c4fe46ccff5a3d9ac"
          },
          {
            "file": "frame-12.jpg",
            "index": 479,
            "time_seconds": 15.966667,
            "sha256": "5a61d35185451749c1d7ee6749aee10d58583d666a3a72784c2d45b29bdd2e57"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "3cb7da5b4a44662ae3b6c730acd1f1e240f394ec3c894475f241a18f0d8769dd"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-ccr-loop-complex",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-ccr-loop-complex",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-ccr-loop-complex.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-ccr-loop-complex/"
    },
    {
      "video": "content/drafts/animations/symmetry-xk-fourier-complex.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-xk-fourier-complex.mp4",
      "basename": "symmetry-xk-fourier-complex.mp4",
      "slug": "symmetry-xk-fourier-complex",
      "chapter_section": "Position / Wave Number Uncertainty",
      "caption": "Trading off spread in position for spread in wave number",
      "image_description": "A complex wave function and its Fourier transform sweep between the localization extremes",
      "video_link_line": 1029,
      "context_before": [
        {
          "start_line": 1025,
          "end_line": 1025,
          "markdown": "As we know, a single-mode wave function has a single wave number. But what position does it have? There is no way to answer this as the wave is uniform over all position space. The same statement holds in reverse. A wave packet ideally localized at one position is uniform over all $k$-space. Anywhere in between these extremes, as a wave packet is more localized in one space, it is more spread out in the dual space. "
        }
      ],
      "context_after": [
        {
          "start_line": 1033,
          "end_line": 1033,
          "markdown": "We can easily see this relationship drawn on paper, but we also hear it in music. The precise pitch of a tuning fork requires long sustain, while the percussive clap of a clave has no clear pitch. This tradeoff is the root of the Heisenberg uncertainty principle, or what is known in popular science as \"quantum fuzziness.\""
        },
        {
          "start_line": 1035,
          "end_line": 1035,
          "markdown": "With this visual understanding of the tradeoff in the spread of the magnitude envelopes in $x$ and $k$ space, we can define the corresponding statistical standard deviation, or uncertainty, in position and wave number."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_xk_fourier_complex.py",
        "scripts/generate_symmetry_packet_phase_modes.py"
      ],
      "generation_note": "Main script --render implements the complex Gaussian Fourier-pair sweep. The packet-phase module supplies shared drawing helpers and libraries; no earlier video is consumed. --check and --encoded-check write the companion validation.",
      "evidence": "Exact NAME match. Matching validation records the full complex Fourier check and sigma_x sweep, 360 frames, 30 fps, 1440 x 900, 12 s; encoded-validation.json confirms these video dimensions and compares decoded samples with source frames.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_xk_fourier_complex.py",
          "file": "sources/scripts/generate_symmetry_xk_fourier_complex.py.txt",
          "sha256": "bc828211e054f4da43a198d4ee8fa8fe93db2da0258786da8a9b2bb01b4015de",
          "bytes": 12858,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_xk_fourier_complex.py"
        },
        {
          "path": "scripts/generate_symmetry_packet_phase_modes.py",
          "file": "sources/scripts/generate_symmetry_packet_phase_modes.py.txt",
          "sha256": "47fdf5353988db4807c5e9a21545b3f5dc44b097d7a5b077eec2889333f57edb",
          "bytes": 15777,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_packet_phase_modes.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-xk-fourier-complex-validation.json",
          "file": "symmetry-xk-fourier-complex/checks/symmetry-xk-fourier-complex-validation.json",
          "sha256": "4db68f1f87d59fd57b1412af48a31160dde56d62457b617df3cd80071b251aef",
          "bytes": 503
        },
        {
          "path": "content/drafts/animations/symmetry-xk-fourier-complex-encoded-validation.json",
          "file": "symmetry-xk-fourier-complex/checks/symmetry-xk-fourier-complex-encoded-validation.json",
          "sha256": "489d51531de2926d4c2511acc0c3247b6b7290537c74c5b9de2b001f7de93f69",
          "bytes": 145
        }
      ],
      "manifest": "symmetry-xk-fourier-complex/manifest.json",
      "manifest_sha256": "161e3cec175807afa210fecbf01e64f53cb09f1ec3f4afcc8fce3fabd78ae7b6",
      "frame_directory": "symmetry-xk-fourier-complex/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-xk-fourier-complex.mp4",
        "video_sha256": "3813ae79823f4c00f436d383dc644323c185560bfbb6e1625ce41f7bf17026fa",
        "duration_seconds": 12.0,
        "fps": 30.0,
        "frame_count": 360,
        "width": 1440,
        "height": 900,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "29d8fc40ff87e572c97faade1c0504ab3a8327d5d16c5f89c6f7e93a952bae8a"
          },
          {
            "file": "frame-02.jpg",
            "index": 33,
            "time_seconds": 1.1,
            "sha256": "ca18cc99f055ebd51db583cfd7681b883f4cee41b8bdc5658577b4404f203c65"
          },
          {
            "file": "frame-03.jpg",
            "index": 65,
            "time_seconds": 2.166667,
            "sha256": "e4f40752c6a57f2b6837f64caf23c6fdadcee507921c6bcf8fe148c3a642b2b5"
          },
          {
            "file": "frame-04.jpg",
            "index": 98,
            "time_seconds": 3.266667,
            "sha256": "04cd36cea68586da35fe3a001083fb15a974e8dc5b723c4760522b694a475a60"
          },
          {
            "file": "frame-05.jpg",
            "index": 131,
            "time_seconds": 4.366667,
            "sha256": "a17c24e7458c3a06a4e8588c0ec90180c933c67d971c804db0beb0d2697cae4a"
          },
          {
            "file": "frame-06.jpg",
            "index": 163,
            "time_seconds": 5.433333,
            "sha256": "aba6c442bc060e406c6a6e80b04193aa8121ef6d139019de024114de5113644c"
          },
          {
            "file": "frame-07.jpg",
            "index": 196,
            "time_seconds": 6.533333,
            "sha256": "f74eedf5b0d4163e34bcd5704f8e451c7fd2d777899a0e3b2bc14cdf88cd8fc5"
          },
          {
            "file": "frame-08.jpg",
            "index": 228,
            "time_seconds": 7.6,
            "sha256": "6ea860c7045bdaf12b6400ef97fd7d712bc9020e3e5a13c162143091dd03576f"
          },
          {
            "file": "frame-09.jpg",
            "index": 261,
            "time_seconds": 8.7,
            "sha256": "66ca4ec63a9060c0bb1f0589ee655a0bc0615e42c1caa4dec01d9a2f8c6cd52f"
          },
          {
            "file": "frame-10.jpg",
            "index": 294,
            "time_seconds": 9.8,
            "sha256": "c3047f678e7c7cff5a9d04f0adf1776dacd949b48333806046c2026f35204c7a"
          },
          {
            "file": "frame-11.jpg",
            "index": 326,
            "time_seconds": 10.866667,
            "sha256": "5a77ea76552e0d3722215bca3c59a99cd673126cceda71aa4d47dfce38834a78"
          },
          {
            "file": "frame-12.jpg",
            "index": 359,
            "time_seconds": 11.966667,
            "sha256": "312d4c154628943c053a005fe868b98c7d04b3bfc177f36c76e9afd7debbada3"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "e969a14159ba3c36d81cc5277c549d89e0ad25bde851d33cb5cf6498c38407ac"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-xk-fourier-complex",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-xk-fourier-complex",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-xk-fourier-complex.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-xk-fourier-complex/"
    },
    {
      "video": "content/drafts/animations/symmetry-xk-squared-magnitudes.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-xk-squared-magnitudes.mp4",
      "basename": "symmetry-xk-squared-magnitudes.mp4",
      "slug": "symmetry-xk-squared-magnitudes",
      "chapter_section": "Position / Wave Number Uncertainty",
      "caption": "Trade-off in uncertainty of position and wave number",
      "image_description": "The squared magnitudes trade position and wave-number widths",
      "video_link_line": 1064,
      "context_before": [
        {
          "start_line": 1057,
          "end_line": 1059,
          "markdown": "```math\n\\Delta x\\,\\Delta k\\ge\\frac12.\n```"
        },
        {
          "start_line": 1060,
          "end_line": 1060,
          "markdown": "This derivation is rather involved, but we can get a feel for the result by plotting the squared magnitude of a wave function and labelling the standard deviation."
        }
      ],
      "context_after": [],
      "sources": [
        "scripts/generate_symmetry_xk_squared_magnitudes_animation.py",
        "scripts/generate_symmetry_xk_uncertainty_extremes_animation.py"
      ],
      "generation_note": "Run the main script without arguments. It draws squared-magnitude frames and uses the imported uncertainty-extremes module for parameters, geometry, fonts, and helpers. It encodes a temporary MP4, replaces the canonical file, then decodes all frames and checks dimensions/fps/count. No earlier movie is consumed.",
      "evidence": "The main script's NAME and render() destination match the linked movie; imported constants specify 1280 x 720, 24 fps, 12 s (288 frames). No matching validation JSON is present; the generator prints its decoded-video verification rather than saving a report. Attribution is by source/output name and pipeline, not a recorded movie hash.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_xk_squared_magnitudes_animation.py",
          "file": "sources/scripts/generate_symmetry_xk_squared_magnitudes_animation.py.txt",
          "sha256": "6ce05c5954a5de3106b1500c742b9bf6153fe826ab0801247f4f30780523ff9f",
          "bytes": 13936,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_xk_squared_magnitudes_animation.py"
        },
        {
          "path": "scripts/generate_symmetry_xk_uncertainty_extremes_animation.py",
          "file": "sources/scripts/generate_symmetry_xk_uncertainty_extremes_animation.py.txt",
          "sha256": "f6653171c7475bd8be34aa4f79b859e9bac6da7c5051aa3a8ef0738c88b6428d",
          "bytes": 20315,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_xk_uncertainty_extremes_animation.py"
        }
      ],
      "generator_checks": [],
      "manifest": "symmetry-xk-squared-magnitudes/manifest.json",
      "manifest_sha256": "630149c96bc2694eb47711dcc282dc3d9eab748e25acec74187d3ec78397a7cb",
      "frame_directory": "symmetry-xk-squared-magnitudes/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-xk-squared-magnitudes.mp4",
        "video_sha256": "889f48b662914160814cd9c0c37c79c5a10525ecab89235e202f93c4d21563be",
        "duration_seconds": 12.0,
        "fps": 24.0,
        "frame_count": 288,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "dfced985d46f702af0b3a2d9daeedd3231e0da68f957ce45e1f870ec605ff89a"
          },
          {
            "file": "frame-02.jpg",
            "index": 26,
            "time_seconds": 1.083333,
            "sha256": "2cfe1ed16b08fd80242bad087fd08e15067a34d0c151aa5cd5c3b57312ebf664"
          },
          {
            "file": "frame-03.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "de115e7c6ca80b20ff17140d6e8c9e8316c7e8b53903a68c03032642dcf2f907"
          },
          {
            "file": "frame-04.jpg",
            "index": 78,
            "time_seconds": 3.25,
            "sha256": "ece4f5e25b34888ded26b9b383c65b2c475f74f14c41ee519677bcbec38bd4bd"
          },
          {
            "file": "frame-05.jpg",
            "index": 104,
            "time_seconds": 4.333333,
            "sha256": "5e1e43d0d851042d67a66fd01ce141360f0df66d76d23e1198db7e189ac84422"
          },
          {
            "file": "frame-06.jpg",
            "index": 130,
            "time_seconds": 5.416667,
            "sha256": "bd5652784170c3f621cf575af98a4b6920aef62b50e863a648314727df7fc34a"
          },
          {
            "file": "frame-07.jpg",
            "index": 157,
            "time_seconds": 6.541667,
            "sha256": "598a3b1185ac3d603c339c19ca60a279ad5c7c758b64f2da59bb2166b7cff55f"
          },
          {
            "file": "frame-08.jpg",
            "index": 183,
            "time_seconds": 7.625,
            "sha256": "3adde7a19fc99fe69b02e5c3bdfdae3077b7e237aafa1a562708ca69bce7404c"
          },
          {
            "file": "frame-09.jpg",
            "index": 209,
            "time_seconds": 8.708333,
            "sha256": "7a342c01a18e4f52075f07806309bcdb087e9b162c36b97decd3051997c122e6"
          },
          {
            "file": "frame-10.jpg",
            "index": 235,
            "time_seconds": 9.791667,
            "sha256": "03547f57627392e14214bb97dabe4f646f387e0f4446306a9e23ead9beddf3d0"
          },
          {
            "file": "frame-11.jpg",
            "index": 261,
            "time_seconds": 10.875,
            "sha256": "5057b02e524b4c9549189e162ecacb421a0b5a95ffa326430944b44ecc45bb66"
          },
          {
            "file": "frame-12.jpg",
            "index": 287,
            "time_seconds": 11.958333,
            "sha256": "5057b02e524b4c9549189e162ecacb421a0b5a95ffa326430944b44ecc45bb66"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "94ca2baf04f516f16f5bb9da94ed93239081ea815c4abcea0edf0c075a8fd40d"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-xk-squared-magnitudes",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-xk-squared-magnitudes",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-xk-squared-magnitudes.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-xk-squared-magnitudes/"
    },
    {
      "video": "content/drafts/animations/symmetry-short-wave-beams.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-short-wave-beams.mp4",
      "basename": "symmetry-short-wave-beams.mp4",
      "slug": "symmetry-short-wave-beams",
      "chapter_section": "Wave Propagation and Interference",
      "caption": "Interference and Rays",
      "image_description": "Waves through fixed openings becoming narrow beams as the wavelength decreases",
      "video_link_line": 1079,
      "context_before": [
        {
          "start_line": 1071,
          "end_line": 1073,
          "markdown": "```math\nx = x_0 + v_0t + \\frac{1}{2}at^2\n```"
        },
        {
          "start_line": 1075,
          "end_line": 1075,
          "markdown": "Such \"physically valid\" paths in the macroscopic world have a fascinating quality that can be leveraged to find the laws of motion that predict them. They are such that some quantity associated with possible paths, which is called **action**, is extremized at the valid path. In the next sections, we will explore the relationship between objects following paths and wave propagation. Just as an object following a definite path extremizes action, a wave, in the regime where it behaves like a ray, follows a path that extremizes accumulated phase. We will establish how contributions from many possible paths combine to produce this behavior, giving us a bridge between wave propagation and action extremization. We will then see how this stationary-path limit connects the quantum wave description we have alluded to with the well-defined paths that action extremization predicts for everyday macroscopic objects."
        }
      ],
      "context_after": [
        {
          "start_line": 1083,
          "end_line": 1083,
          "markdown": "Thus far we have described the symmetry group of a wave with a single translation direction $x$ and wave number, $k$. If the wave is to propagate, we also require that the wave represent time translation. We also need to require that time translation commute with spatial translation, for otherwise, it would change the mode composition over time, and spatial translation would no longer be a symmetry of nature. A single-mode travelling wave is then given by:"
        },
        {
          "start_line": 1085,
          "end_line": 1087,
          "markdown": "```math\nM_0e^{i\\left[kx-\\omega t\\right]}.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_short_wave_beams.py",
        "scripts/generate_symmetry_widening_apertures.py"
      ],
      "generation_note": "Main script --render computes finite-opening angular-spectrum fields and encodes their frames. generate_symmetry_widening_apertures supplies shared visual helpers and imported libraries. --check and --encoded-check write the companion reports; no input movie is used.",
      "evidence": "Exact NAME match. Matching validation records fixed opening 0.5, centers -2.4/+2.4, wavelengths 0.5 down to 0.001, and 1440 x 1120, 30 fps, 20 s. Matching encoded-validation.json records all 600 decoded frames and source-frame comparison errors.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_short_wave_beams.py",
          "file": "sources/scripts/generate_symmetry_short_wave_beams.py.txt",
          "sha256": "aa81d6b00bd42d01531e20b7231a663092d69a81bde399fbec577b9038719cb8",
          "bytes": 11969,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_short_wave_beams.py"
        },
        {
          "path": "scripts/generate_symmetry_widening_apertures.py",
          "file": "sources/scripts/generate_symmetry_widening_apertures.py.txt",
          "sha256": "2850a5c705823e498af5ef6180f0639cde6955631a763bc735e3d8992bb35127",
          "bytes": 15755,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_widening_apertures.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-short-wave-beams-validation.json",
          "file": "symmetry-short-wave-beams/checks/symmetry-short-wave-beams-validation.json",
          "sha256": "49603b342c08ee1d2c9d69254ae9aa80d2b7beae9dab12e1d2f8ed256c5a5a2f",
          "bytes": 721
        },
        {
          "path": "content/drafts/animations/symmetry-short-wave-beams-encoded-validation.json",
          "file": "symmetry-short-wave-beams/checks/symmetry-short-wave-beams-encoded-validation.json",
          "sha256": "6ffc80015366476539ee23aca3149aea5a2b1ab7134d04053cfbdb90849f3c88",
          "bytes": 265
        }
      ],
      "manifest": "symmetry-short-wave-beams/manifest.json",
      "manifest_sha256": "3152a65530828574e2583193f049a3ac12be270bc969033e128bddd837967704",
      "frame_directory": "symmetry-short-wave-beams/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-short-wave-beams.mp4",
        "video_sha256": "0fdf04ec716792f0956e939ca3f6aac349cb0f3315b200b2a6eb8d07c46634bd",
        "duration_seconds": 20.0,
        "fps": 30.0,
        "frame_count": 600,
        "width": 1440,
        "height": 1120,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "f818b50690ba2b78db70b54ba6f8e7f6ba94f1818ed541bb46184f546d59d96f"
          },
          {
            "file": "frame-02.jpg",
            "index": 54,
            "time_seconds": 1.8,
            "sha256": "a7e8cceb6123f9b5116ab2b0ee0368be88878aa9eec650bf0afec4ced87c91ad"
          },
          {
            "file": "frame-03.jpg",
            "index": 109,
            "time_seconds": 3.633333,
            "sha256": "b06f58fcea29e9dfb0b44d85745feb80b40affac154becba05e92ecaf3a65d63"
          },
          {
            "file": "frame-04.jpg",
            "index": 163,
            "time_seconds": 5.433333,
            "sha256": "c6cf5eeae3afed4a2f519df027128dc5e831e5e8899b738a814f465c9e728287"
          },
          {
            "file": "frame-05.jpg",
            "index": 218,
            "time_seconds": 7.266667,
            "sha256": "129d63d0a5aeeb8c97dd73015549013e1d0c85ea4eda649de2ca8fe13da89453"
          },
          {
            "file": "frame-06.jpg",
            "index": 272,
            "time_seconds": 9.066667,
            "sha256": "835ea9a98ca6e45e0ca7610f218856876e17032fe1252b347ddfa66ec31475ac"
          },
          {
            "file": "frame-07.jpg",
            "index": 327,
            "time_seconds": 10.9,
            "sha256": "0a5a536fb412a592a704ecff39f38663e9c91e179f9597de8aaccf5b8452b448"
          },
          {
            "file": "frame-08.jpg",
            "index": 381,
            "time_seconds": 12.7,
            "sha256": "645e5df67bdb29fd7ea51e5480e81948e572b01970186b76d264ad37b7430071"
          },
          {
            "file": "frame-09.jpg",
            "index": 436,
            "time_seconds": 14.533333,
            "sha256": "39287802ac5120586a823eca626fafcb2d2493b375a9ee3688db4fe27f84d633"
          },
          {
            "file": "frame-10.jpg",
            "index": 490,
            "time_seconds": 16.333333,
            "sha256": "b313b2725dc7d06a7bb57f92882a845f0968c0603e1e5e2cb2076fd34c339b51"
          },
          {
            "file": "frame-11.jpg",
            "index": 545,
            "time_seconds": 18.166667,
            "sha256": "d87a4ccf1bc9766b8925a2fa27ff349c07cec018234a176e7f031d9bafa9a75b"
          },
          {
            "file": "frame-12.jpg",
            "index": 599,
            "time_seconds": 19.966667,
            "sha256": "d6d1f5d57416d758be796389e92a3c7529337fbe22d0dc0b54b7f361780dc874"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "1c0a37d753688d2615b1839b36f0c8c348b663b95dee70271dcf746366fc4189"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-short-wave-beams",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-short-wave-beams",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-short-wave-beams.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-short-wave-beams/"
    },
    {
      "video": "content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs.mp4",
      "basename": "symmetry-double-slit-candidate-paths-shortwave-arcs.mp4",
      "slug": "symmetry-double-slit-candidate-paths-shortwave-arcs",
      "chapter_section": "Wave Propagation and Interference",
      "caption": "Two Path Interference",
      "image_description": "The two contributions $ACB$ and $ADB$ from a point source through two narrow openings",
      "video_link_line": 1095,
      "context_before": [
        {
          "start_line": 1089,
          "end_line": 1089,
          "markdown": "Because time is special, $\\omega$ is called (angular) **frequency**, not wave number, but from a mathematical perspective, it is just another wave number."
        },
        {
          "start_line": 1091,
          "end_line": 1091,
          "markdown": "Let us now ask the question: how do we find the amplitude at some point $B$ from some initial state of a wave? To do this, we can decompose the contributions into those from individual paths, starting with a very simple model. First, let's construct a point source of single-mode spherical waves emanating from $A$. Then let's add a barrier with two slits through which the wave can pass, $C$ and $D$. This setup allows us to calculate the amplitude at $B$ by combining only the amplitudes associated with the two paths $ACB$ and $ADB$."
        }
      ],
      "context_after": [
        {
          "start_line": 1099,
          "end_line": 1099,
          "markdown": "First, let's figure out how any one straight segment of a plane wave contributes to the amplitude at its endpoint. From:"
        },
        {
          "start_line": 1101,
          "end_line": 1103,
          "markdown": "```math\ne^{i\\left[kx-\\omega t\\right]}.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_double_slit_candidate_paths_vivid.py",
        "scripts/generate_symmetry_widening_apertures.py"
      ],
      "generation_note": "The vivid generator, whose NAME uses the shortwave-arcs stem, is the actual entrypoint (--render). It uses shared artwork from widening_apertures and renders the two-point-aperture phase illustration directly. --check and --encoded-check produce the companion reports.",
      "evidence": "The vivid script explicitly sets NAME to symmetry-double-slit-candidate-paths-shortwave-arcs. Matching validation records wavelength 0.5 and the same A,C,D,B geometry as the script; matching encoded-validation.json records 300 frames, 30 fps, 1440 x 810, 10 s. notes/worked/double-slit-candidate-paths-vivid-animation.md identifies this output.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_double_slit_candidate_paths_vivid.py",
          "file": "sources/scripts/generate_symmetry_double_slit_candidate_paths_vivid.py.txt",
          "sha256": "1d4eb3263ecd8d430541f6415d985f354769123b54fadc498a451598493f9aff",
          "bytes": 11461,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_double_slit_candidate_paths_vivid.py"
        },
        {
          "path": "scripts/generate_symmetry_widening_apertures.py",
          "file": "sources/scripts/generate_symmetry_widening_apertures.py.txt",
          "sha256": "2850a5c705823e498af5ef6180f0639cde6955631a763bc735e3d8992bb35127",
          "bytes": 15755,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_widening_apertures.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-validation.json",
          "file": "symmetry-double-slit-candidate-paths-shortwave-arcs/checks/symmetry-double-slit-candidate-paths-shortwave-arcs-validation.json",
          "sha256": "c107e456ee7ed53afd28475ef0d50f96d7198f9b11dfe1696ce211255f8fd31d",
          "bytes": 580
        },
        {
          "path": "content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs-encoded-validation.json",
          "file": "symmetry-double-slit-candidate-paths-shortwave-arcs/checks/symmetry-double-slit-candidate-paths-shortwave-arcs-encoded-validation.json",
          "sha256": "0bca767337bc17d6da2d11c9fee2d8c5efee64cab1ab7aac100e250e8182c071",
          "bytes": 264
        }
      ],
      "manifest": "symmetry-double-slit-candidate-paths-shortwave-arcs/manifest.json",
      "manifest_sha256": "d61994dc1d977bb194a34a41e2c714b0849ca4e5faf1a01c6e1d967b90eaa63c",
      "frame_directory": "symmetry-double-slit-candidate-paths-shortwave-arcs/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-double-slit-candidate-paths-shortwave-arcs.mp4",
        "video_sha256": "f567d8740a19e069ed3e38146db5b7a7672924649866e6bbde34c026bebdb688",
        "duration_seconds": 10.0,
        "fps": 30.0,
        "frame_count": 300,
        "width": 1440,
        "height": 810,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "4d4eaeae2aafc0e02d4f24214628edea0fd96e4972044f8bee429dff99097c1d"
          },
          {
            "file": "frame-02.jpg",
            "index": 27,
            "time_seconds": 0.9,
            "sha256": "cd05a7d62739341297a171f9f4b8b2af4585d667650eaeb9c1ece6762bda6237"
          },
          {
            "file": "frame-03.jpg",
            "index": 54,
            "time_seconds": 1.8,
            "sha256": "c49bad34b0f45291446fb4d37e4ba61248fa08fb667abed0a5a7a11a3cd589d2"
          },
          {
            "file": "frame-04.jpg",
            "index": 82,
            "time_seconds": 2.733333,
            "sha256": "fe8cff53ad300fd1440df989a83ec07dba9d0b7766ce64c73bdac57c25ae6131"
          },
          {
            "file": "frame-05.jpg",
            "index": 109,
            "time_seconds": 3.633333,
            "sha256": "93f002bc5bef73d89c06f40b40e0ab9bc6e6760b946dea208013e54c487a6ccf"
          },
          {
            "file": "frame-06.jpg",
            "index": 136,
            "time_seconds": 4.533333,
            "sha256": "55995fa077f06ee66f26def9df6b5d2f40de5c9832b143d522eabad06451ae25"
          },
          {
            "file": "frame-07.jpg",
            "index": 163,
            "time_seconds": 5.433333,
            "sha256": "ef5427380eb68578d5aae3be7435c9c4bd2f17a58710bc48a6f3f2b484f710e0"
          },
          {
            "file": "frame-08.jpg",
            "index": 190,
            "time_seconds": 6.333333,
            "sha256": "db2b3a2b30f56153325debf80da7fe49de68342cd376bc16aac68d5a1c0b3a03"
          },
          {
            "file": "frame-09.jpg",
            "index": 217,
            "time_seconds": 7.233333,
            "sha256": "75b3e2d47b74ba7a08db3801ee92a61b0db7eadfd1ee2213aac9ef43f48ffc37"
          },
          {
            "file": "frame-10.jpg",
            "index": 245,
            "time_seconds": 8.166667,
            "sha256": "63a923a9034cec0da9d1710be9cca5610a5f4f5f7e811661be6a5a07d6618198"
          },
          {
            "file": "frame-11.jpg",
            "index": 272,
            "time_seconds": 9.066667,
            "sha256": "91e2144941d4e9dba977063130f59904773abdb86248c8acbe09daea519dbfee"
          },
          {
            "file": "frame-12.jpg",
            "index": 299,
            "time_seconds": 9.966667,
            "sha256": "903a73aad66d2ec0ad810a532cfaa888d99f55ae4cdddd9398c8392cf043738a"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "95dd8a1f1858a91eb37daec69a125a5c6a5e0646fd482805c4d07b52709145a4"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-double-slit-candidate-paths-shortwave-arcs",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-double-slit-candidate-paths-shortwave-arcs",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-double-slit-candidate-paths-shortwave-arcs.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-double-slit-candidate-paths-shortwave-arcs/"
    },
    {
      "video": "content/drafts/animations/symmetry-many-slit-paths-phasors-interference.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-many-slit-paths-phasors-interference.mp4",
      "basename": "symmetry-many-slit-paths-phasors-interference.mp4",
      "slug": "symmetry-many-slit-paths-phasors-interference",
      "chapter_section": "Wave Propagation and Interference",
      "caption": "Seeing stationarity emerge in closely spaced paths",
      "image_description": "Many paths, their complex sum, and the resulting interference pattern",
      "video_link_line": 1195,
      "context_before": [
        {
          "start_line": 1191,
          "end_line": 1191,
          "markdown": "We can repeat the same procedure with many more paths. As the path deviates more from a straight, minimum length path, it has a greater first-order change in phase. (This is the common result from calculus that near a function's minimum, there is no change to the value of the function in the first order of the argument.) When the candidate paths are far from the stationary value, their phases vary greatly, effectively cancelling out their contributions to the total sum. On the other hand, the phases of the paths near the stationary path align and dominate the sum. The green line in the tip-to-tail pane of the animation shows the sum of each of these contributions, giving the amplitude at $B$. The resulting intensity on the projection screen is the square of this magnitude."
        }
      ],
      "context_after": [
        {
          "start_line": 1199,
          "end_line": 1199,
          "markdown": "We can extend this procedure to its limit and include infinitely many screens with infinitely many slits, and recover unobstructed propagation. In the following animation, we start with a plane wave and recover that same wave. This construction, which provides a bridge to Feynman’s path integral formulation of quantum mechanics, has its roots in Huygens’ wavelets from the late 1600s, extended by Fresnel to include interference in the early 1800s."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_many_slit_paths_phasors_interference.py",
        "scripts/generate_symmetry_desktop_wave_bookends.py",
        "scripts/generate_symmetry_slit_tip_to_tail.py",
        "scripts/perforated_tip_to_tail_model.py",
        "scripts/corrected_tip_to_tail_model.py",
        "scripts/generate_symmetry_wave_paths_explainer.py",
        "scripts/generate_symmetry_step2_packet_summary_point.py"
      ],
      "generation_note": "The same-named canonical entrypoint's main() now delegates to generate_symmetry_desktop_wave_bookends.render_canonical_desktop(); its older draw_frame helpers alone do not reproduce the accepted movie. V7 adds a 96-frame intensity-colored wave intro to a 444-frame centered 49-slit body, then concatenates with FFmpeg stream copy. The recorded build reused content/drafts/animations/iterations/symmetry-many-slit-paths-phasors-interference-20260911T150313560139Z-centered-body.mp4, with its .model.json record; running the canonical entrypoint regenerates that body, while the bookends script supports --reuse-middle. Shared geometry imports pass through the old canonical helpers, wave_paths_explainer, and step2_packet_summary_point. All five main V7 implementation/model files are byte-identical to their scripts/iterations/*-20260911T155628136531Z-v7.py snapshots.",
      "evidence": "Canonical validation identifies iterations/symmetry-many-slit-paths-phasors-interference-v7-intensity-in-the-wave.mp4, 540 frames, 24 fps, 22.5 s, 1280 x 720, the exact body input, and middle_decoded_frames_identical=true. The current MP4 SHA-256 matches its recorded video_sha256 (05a021cdde8ada00ecd1e43d59d990d0be25a7892cc0cced8b7e047303be2a3e). notes/worked/desktop-wave-intensity-field.md confirms the canonical entrypoint and V7 implementation.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_many_slit_paths_phasors_interference.py",
          "file": "sources/scripts/generate_symmetry_many_slit_paths_phasors_interference.py.txt",
          "sha256": "4b8845f4529bdb1c8643be72b3b415cd305bc01ac54cda304bac840529eb2b54",
          "bytes": 18588,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_many_slit_paths_phasors_interference.py"
        },
        {
          "path": "scripts/generate_symmetry_desktop_wave_bookends.py",
          "file": "sources/scripts/generate_symmetry_desktop_wave_bookends.py.txt",
          "sha256": "b6ff52349323d0ce825fad7fc091664b3a3c1a4e166ec23eb2ef605316b1c0ba",
          "bytes": 23198,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_desktop_wave_bookends.py"
        },
        {
          "path": "scripts/generate_symmetry_slit_tip_to_tail.py",
          "file": "sources/scripts/generate_symmetry_slit_tip_to_tail.py.txt",
          "sha256": "a75b1c5c5d966911d6a031e3d8c8826acefb18dce40b5171590896ce0d1b4d6d",
          "bytes": 18706,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_slit_tip_to_tail.py"
        },
        {
          "path": "scripts/perforated_tip_to_tail_model.py",
          "file": "sources/scripts/perforated_tip_to_tail_model.py.txt",
          "sha256": "4c1f497fd6b3508bca022da68099561cb8900868511d99510b2a5e6ceff7c4a7",
          "bytes": 6193,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/perforated_tip_to_tail_model.py"
        },
        {
          "path": "scripts/corrected_tip_to_tail_model.py",
          "file": "sources/scripts/corrected_tip_to_tail_model.py.txt",
          "sha256": "6f399befabb8d5cbb22f120d00db405a1aafc513287a8a878c755f0be3d834c9",
          "bytes": 6738,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/corrected_tip_to_tail_model.py"
        },
        {
          "path": "scripts/generate_symmetry_wave_paths_explainer.py",
          "file": "sources/scripts/generate_symmetry_wave_paths_explainer.py.txt",
          "sha256": "aa6b8924891f48279963539ebd9e5134bb18a1e58c112ac5c40f96c09a6eb111",
          "bytes": 32421,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_wave_paths_explainer.py"
        },
        {
          "path": "scripts/generate_symmetry_step2_packet_summary_point.py",
          "file": "sources/scripts/generate_symmetry_step2_packet_summary_point.py.txt",
          "sha256": "9286e2052586ef1958124f5d77ac0a3228eea6f8d9c738b382bdbe51f9d0eb0a",
          "bytes": 13145,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_step2_packet_summary_point.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-many-slit-paths-phasors-interference-validation.json",
          "file": "symmetry-many-slit-paths-phasors-interference/checks/symmetry-many-slit-paths-phasors-interference-validation.json",
          "sha256": "ec7c61f355e16478b5afbc1d6e48c69ad556e345eb651e9f855e1446e9a35bd7",
          "bytes": 7398
        }
      ],
      "manifest": "symmetry-many-slit-paths-phasors-interference/manifest.json",
      "manifest_sha256": "08cf2ae1fafa94822ad393daf2c693a2502fc4d523cc967800cea211c399afb7",
      "frame_directory": "symmetry-many-slit-paths-phasors-interference/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-many-slit-paths-phasors-interference.mp4",
        "video_sha256": "05a021cdde8ada00ecd1e43d59d990d0be25a7892cc0cced8b7e047303be2a3e",
        "duration_seconds": 22.5,
        "fps": 24.0,
        "frame_count": 540,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "b05dcc64824397f088f2698a793fe73ac80c9a25904c9e38cc1ed0231f1d9f0b"
          },
          {
            "file": "frame-02.jpg",
            "index": 49,
            "time_seconds": 2.041667,
            "sha256": "22037e45edc22ddc11813dc120e0e78e24a42e4797d4e977b82efe299b096742"
          },
          {
            "file": "frame-03.jpg",
            "index": 98,
            "time_seconds": 4.083333,
            "sha256": "d1b28c020a227a177bb3c3c3839b65eec5f6409355fcc4329705e01001e389ec"
          },
          {
            "file": "frame-04.jpg",
            "index": 147,
            "time_seconds": 6.125,
            "sha256": "1161aa7e922e4f4bd3d93d986f3220b6dde0414e46f0ceec8285ede8b2495106"
          },
          {
            "file": "frame-05.jpg",
            "index": 196,
            "time_seconds": 8.166667,
            "sha256": "98ea998495d431616a6d36b394daae3df0a4b2d692b295441af6c18b6693ceef"
          },
          {
            "file": "frame-06.jpg",
            "index": 245,
            "time_seconds": 10.208333,
            "sha256": "993234919266c208cc1d576772c8e9b8953b8ba6bc629a2dfc857c8e406ddf4e"
          },
          {
            "file": "frame-07.jpg",
            "index": 294,
            "time_seconds": 12.25,
            "sha256": "0969ddf2e16bd5d799599b1f14f9c8a5acb240741e60318df1cdaea860cfa06e"
          },
          {
            "file": "frame-08.jpg",
            "index": 343,
            "time_seconds": 14.291667,
            "sha256": "cc605c879f08f5b900ad6e8208fddeae03798112f22859474ec6fb0170762734"
          },
          {
            "file": "frame-09.jpg",
            "index": 392,
            "time_seconds": 16.333333,
            "sha256": "2b920e891cd8abd70fff29466374d678e8de5d32f40604efd83987531a593ae2"
          },
          {
            "file": "frame-10.jpg",
            "index": 441,
            "time_seconds": 18.375,
            "sha256": "ff50976a486fb8fe3f216f5f529f8471815e420d8fac49111a02725b799fe74d"
          },
          {
            "file": "frame-11.jpg",
            "index": 490,
            "time_seconds": 20.416667,
            "sha256": "315ddfa03ceb30079036634fad10900f9b83fce290b6151d65467b692b4cd8b6"
          },
          {
            "file": "frame-12.jpg",
            "index": 539,
            "time_seconds": 22.458333,
            "sha256": "412a7fd2c24f1474dc53a761437852801df8327014c70ca518dd0a923475f9d2"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "da8f45f21b19503e674044f7a7cdd92235fe70ce7a6d66e12d69e5994d969991"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-many-slit-paths-phasors-interference",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-many-slit-paths-phasors-interference",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-many-slit-paths-phasors-interference.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-many-slit-paths-phasors-interference/"
    },
    {
      "video": "content/drafts/animations/symmetry-schematic-screens-v3-concise.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-schematic-screens-v3-concise.mp4",
      "basename": "symmetry-schematic-screens-v3-concise.mp4",
      "slug": "symmetry-schematic-screens-v3-concise",
      "chapter_section": "Wave Propagation and Interference",
      "caption": "Huygens–Fresnel principle",
      "image_description": "Huygens wavelets and their coherent sum as slits and screens are added",
      "video_link_line": 1203,
      "context_before": [
        {
          "start_line": 1199,
          "end_line": 1199,
          "markdown": "We can extend this procedure to its limit and include infinitely many screens with infinitely many slits, and recover unobstructed propagation. In the following animation, we start with a plane wave and recover that same wave. This construction, which provides a bridge to Feynman’s path integral formulation of quantum mechanics, has its roots in Huygens’ wavelets from the late 1600s, extended by Fresnel to include interference in the early 1800s."
        }
      ],
      "context_after": [
        {
          "start_line": 1207,
          "end_line": 1207,
          "markdown": "Let us now ask one more question. What happens to our tip-to-tail diagram of the path contributions when we vary the wavelength relative to the slit? As the wavelength becomes small, even a slight change in path length can produce a large phase change:"
        },
        {
          "start_line": 1209,
          "end_line": 1212,
          "markdown": "```math\n\\phi=\\frac{2\\pi L}{\\lambda}=2\\pi n+\\theta,\n\\qquad 0\\leq\\theta<2\\pi\n```"
        }
      ],
      "sources": [
        "scripts/retime_symmetry_schematic_screens.py",
        "scripts/generate_symmetry_schematic_screens_v3.py",
        "scripts/schematic_screens_v3_model.py",
        "scripts/generate_symmetry_schematic_screens_v2.py",
        "scripts/generate_symmetry_schematic_screens.py",
        "scripts/generate_symmetry_plane_wave_screens.py",
        "scripts/plane_wave_screens_model.py"
      ],
      "generation_note": "Final entrypoint is retime_symmetry_schematic_screens.py, which consumes content/drafts/animations/symmetry-schematic-screens-v3.mp4. Generate that 52-second source with generate_symmetry_schematic_screens_v3.py --render; its listed local imports supply construction state, field model, and drawing helpers. The final script applies whole-frame monotone FFmpeg setpts/fps retiming to 24 seconds, without recomputing fields or interpolating frames. Source-to-output knots are 0:0, 2:1, 22:7, 42:17, 48:21, 52:24 seconds.",
      "evidence": "The concise validation explicitly names the v3 input and retiming method and records 576 frames, 24 fps, 1280 x 720, 24 s. Both current movies match its source_sha256 and video_sha256 fields (source 4fca2f3e85cddef6ae2e85b0fbb400b157c807dfebdb86cc9df76be8cf298da2; final 66887f22e7e0dae166c0e097a20ef5256c28be0e9192eb803e6af168e0d0a24b).",
      "source_files": [
        {
          "path": "scripts/retime_symmetry_schematic_screens.py",
          "file": "sources/scripts/retime_symmetry_schematic_screens.py.txt",
          "sha256": "d6801c0b61fb87e9e202633c31b0fc5853268614fef2db5ce5846f8365de4db9",
          "bytes": 5271,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/retime_symmetry_schematic_screens.py"
        },
        {
          "path": "scripts/generate_symmetry_schematic_screens_v3.py",
          "file": "sources/scripts/generate_symmetry_schematic_screens_v3.py.txt",
          "sha256": "fdba8977d904d4bea5de26b701e095c01e3a24e2fe79ab65251d7fcfa3d3d615",
          "bytes": 17433,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_schematic_screens_v3.py"
        },
        {
          "path": "scripts/schematic_screens_v3_model.py",
          "file": "sources/scripts/schematic_screens_v3_model.py.txt",
          "sha256": "c094ccf24f356709e2ff2a103ecad4ae716c2bafbcbc7f2040c41c106bb3f7fa",
          "bytes": 5796,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/schematic_screens_v3_model.py"
        },
        {
          "path": "scripts/generate_symmetry_schematic_screens_v2.py",
          "file": "sources/scripts/generate_symmetry_schematic_screens_v2.py.txt",
          "sha256": "e5d7fa0d52eefbcfb839dbc48565299901d49914fc9f48fd1aaab548f842c2ba",
          "bytes": 10205,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_schematic_screens_v2.py"
        },
        {
          "path": "scripts/generate_symmetry_schematic_screens.py",
          "file": "sources/scripts/generate_symmetry_schematic_screens.py.txt",
          "sha256": "6e31805fd5a4de5bd4b19ed3c2a6a035ca6458e2389dab5162a0567186b07977",
          "bytes": 13719,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_schematic_screens.py"
        },
        {
          "path": "scripts/generate_symmetry_plane_wave_screens.py",
          "file": "sources/scripts/generate_symmetry_plane_wave_screens.py.txt",
          "sha256": "0085a1a626f9ba5bfb88cb22974b4c65692dddc7afcf97165b9751c320331381",
          "bytes": 8577,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_plane_wave_screens.py"
        },
        {
          "path": "scripts/plane_wave_screens_model.py",
          "file": "sources/scripts/plane_wave_screens_model.py.txt",
          "sha256": "627e82993ba8de74005677c6dc5b5afafe8f3a801a8b0ebde7ffd368b51ea420",
          "bytes": 8453,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/plane_wave_screens_model.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-schematic-screens-v3-concise-validation.json",
          "file": "symmetry-schematic-screens-v3-concise/checks/symmetry-schematic-screens-v3-concise-validation.json",
          "sha256": "e1991adcb42c874b12663ad78e83e353d3e9eb8a4c5420d9454a8a236d647b55",
          "bytes": 1133
        }
      ],
      "manifest": "symmetry-schematic-screens-v3-concise/manifest.json",
      "manifest_sha256": "84a7a319502fda4030736747a2f223180810c60bc3ebd7cbf006c4ebf85e41b9",
      "frame_directory": "symmetry-schematic-screens-v3-concise/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-schematic-screens-v3-concise.mp4",
        "video_sha256": "66887f22e7e0dae166c0e097a20ef5256c28be0e9192eb803e6af168e0d0a24b",
        "duration_seconds": 24.0,
        "fps": 24.0,
        "frame_count": 576,
        "width": 1280,
        "height": 720,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "0604fabf0ba2ae5495485c157333e70151441ab93aaf9578dde9469d75109425"
          },
          {
            "file": "frame-02.jpg",
            "index": 52,
            "time_seconds": 2.166667,
            "sha256": "9bf5f5e55c81368ec9f5198cb367603cbb0daa8b51ece44cdbce187297f394c6"
          },
          {
            "file": "frame-03.jpg",
            "index": 105,
            "time_seconds": 4.375,
            "sha256": "8e98e5c8321ff2c5f424413af6b14b98c46801d0235ef01dadd2df863c1d0036"
          },
          {
            "file": "frame-04.jpg",
            "index": 157,
            "time_seconds": 6.541667,
            "sha256": "63c9e53cd8d6123bef7cfffad0c4bdcf024986f504142bfbaeb6fe64dbf93766"
          },
          {
            "file": "frame-05.jpg",
            "index": 209,
            "time_seconds": 8.708333,
            "sha256": "95e153a2b7e88c5e91228dcff819913db2382b4077d2946e3b803067edddaf76"
          },
          {
            "file": "frame-06.jpg",
            "index": 261,
            "time_seconds": 10.875,
            "sha256": "41c507ee48edc18e8bc84316dc30258fb9fa15cdccaaa5a945dea1cad355e013"
          },
          {
            "file": "frame-07.jpg",
            "index": 314,
            "time_seconds": 13.083333,
            "sha256": "a99326635c3becf28ba8429aba6d4c272c68ec166653b999a1e007472886bc44"
          },
          {
            "file": "frame-08.jpg",
            "index": 366,
            "time_seconds": 15.25,
            "sha256": "25a0760f1a45f4c9c7ff7f8be58f32768cbdbfc2a8b3f6097c7428fbe9a94b67"
          },
          {
            "file": "frame-09.jpg",
            "index": 418,
            "time_seconds": 17.416667,
            "sha256": "fb31db31f57922c69778b5b58ca83f484f38ed5fc2adb8e36e8d33965487686e"
          },
          {
            "file": "frame-10.jpg",
            "index": 470,
            "time_seconds": 19.583333,
            "sha256": "fc2ac75d1d2cc7421f0a43045360a8726d3fc332f114a89e19330a19b9cab840"
          },
          {
            "file": "frame-11.jpg",
            "index": 523,
            "time_seconds": 21.791667,
            "sha256": "b295d8c8b696d3d042f9be176ff3ceb6baad3c6cee7d5aebfec27ee17c69f1fb"
          },
          {
            "file": "frame-12.jpg",
            "index": 575,
            "time_seconds": 23.958333,
            "sha256": "59e96b9b69327325c7f67f24804a9bf08609e9aa880f073fc311498ed12ea466"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "045def18ab87a9d3a4d027b6af6228bd162c80a244651088bae2eeb266cf8eed"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-schematic-screens-v3-concise",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-schematic-screens-v3-concise",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-schematic-screens-v3-concise.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-schematic-screens-v3-concise/"
    },
    {
      "video": "content/drafts/animations/symmetry-phase-remainder-spinners.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-phase-remainder-spinners.mp4",
      "basename": "symmetry-phase-remainder-spinners.mp4",
      "slug": "symmetry-phase-remainder-spinners",
      "chapter_section": "Wave Propagation and Interference",
      "caption": null,
      "image_description": "Three trials accumulate a quarter turn at the longer wavelength while the shorter wavelength produces many rotations",
      "video_link_line": 1218,
      "context_before": [
        {
          "start_line": 1209,
          "end_line": 1212,
          "markdown": "```math\n\\phi=\\frac{2\\pi L}{\\lambda}=2\\pi n+\\theta,\n\\qquad 0\\leq\\theta<2\\pi\n```"
        },
        {
          "start_line": 1214,
          "end_line": 1214,
          "markdown": "Away from a stationary path, the phase winds through many cycles over a small range of paths, leaving an effectively random phase remainder so that contributions away from stationary paths largely cancel, while those near stationary paths dominate the sum."
        }
      ],
      "context_after": [
        {
          "start_line": 1220,
          "end_line": 1220,
          "markdown": "In this limit, waves passing through slits behave as rays. A narrow beam through one opening reaches a small region of the screen, much as a thrown ball does."
        },
        {
          "start_line": 1222,
          "end_line": 1222,
          "markdown": "Each path shares its color with its contribution to the tip-to-tail sum. As the wavelength shrinks, an increasingly narrow range of paths around the straight path carries the sum forward."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_phase_remainder_spinners.py",
        "scripts/generate_symmetry_two_tip_to_tail_limits.py"
      ],
      "generation_note": "Use the current main script --render, with --check and --encoded-check for validation. It imports general drawing/encoding helpers from two_tip_to_tail_limits. Archived phase-remainder v1/v2 scripts and movies are earlier variants; the current generator shows left endpoints at 30,60,90 degrees.",
      "evidence": "Matching validation records lengths 1,2,3, wavelengths 12 and 5/47, right remainders 144,288,72 degrees, 1020 frames at 60 fps, 1440 x 870, 17 s. Matching encoded-validation.json confirms all 1020 frames. notes/worked/phase-remainder-spinners.md identifies the current generator and explicitly distinguishes the archived v1/v2 variants.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_phase_remainder_spinners.py",
          "file": "sources/scripts/generate_symmetry_phase_remainder_spinners.py.txt",
          "sha256": "ab2938d03e2a08d4e00e7294e353ba38d863d74b3ec27fef0d77c3ec5c0fdd42",
          "bytes": 13070,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_phase_remainder_spinners.py"
        },
        {
          "path": "scripts/generate_symmetry_two_tip_to_tail_limits.py",
          "file": "sources/scripts/generate_symmetry_two_tip_to_tail_limits.py.txt",
          "sha256": "9a7e8081dc0333b30ba0d85cd49552af8cc9377a65b2bae582b7d1c5043bae12",
          "bytes": 11631,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_two_tip_to_tail_limits.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-phase-remainder-spinners-validation.json",
          "file": "symmetry-phase-remainder-spinners/checks/symmetry-phase-remainder-spinners-validation.json",
          "sha256": "8112603c8770b320ff3f83bfab3c4fe8119cbd6fe082d1cc6e7f3e874872cf61",
          "bytes": 1112
        },
        {
          "path": "content/drafts/animations/symmetry-phase-remainder-spinners-encoded-validation.json",
          "file": "symmetry-phase-remainder-spinners/checks/symmetry-phase-remainder-spinners-encoded-validation.json",
          "sha256": "5ab818c503620129176f021755901d3af0b68b490ae46f5669ba1acb8e55eab4",
          "bytes": 2736
        }
      ],
      "manifest": "symmetry-phase-remainder-spinners/manifest.json",
      "manifest_sha256": "425eda7c0677c4640b6a325eefe3a9f84e6613aaf3d057d4308f911023a34cd9",
      "frame_directory": "symmetry-phase-remainder-spinners/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-phase-remainder-spinners.mp4",
        "video_sha256": "bb3a49c2b2cb17d33c7a5e67dd057eb1efa4c07e4f7d06390caad1214601360a",
        "duration_seconds": 17.0,
        "fps": 60.0,
        "frame_count": 1020,
        "width": 1440,
        "height": 870,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "8015eae8365191147c05122e42690d67c872c3f359e9426af1bc759dbf88fffd"
          },
          {
            "file": "frame-02.jpg",
            "index": 93,
            "time_seconds": 1.55,
            "sha256": "e9e70e4e0a13aeb495658245cc7c1880ee036838d43703e542c06dc94a3b75e9"
          },
          {
            "file": "frame-03.jpg",
            "index": 185,
            "time_seconds": 3.083333,
            "sha256": "aefca65f27171bf3b62cae3977e63461ac94459587822036ccfa1d02a819f11f"
          },
          {
            "file": "frame-04.jpg",
            "index": 278,
            "time_seconds": 4.633333,
            "sha256": "8a15c607abd48608d2a0da3a3410a3a08d7622d0532c3ea8dc103ae9af71a042"
          },
          {
            "file": "frame-05.jpg",
            "index": 371,
            "time_seconds": 6.183333,
            "sha256": "61b84769042da8b287308685ffd7aa57092f432bf668dbf5488eacc3311a66bf"
          },
          {
            "file": "frame-06.jpg",
            "index": 463,
            "time_seconds": 7.716667,
            "sha256": "2a0909e51a46fb2a7a7246d9ef98ea1502c28eb0816ee33f2d55c42ae8fe4c9d"
          },
          {
            "file": "frame-07.jpg",
            "index": 556,
            "time_seconds": 9.266667,
            "sha256": "1e2602263d0e030c5173822dfd1562d12c6938103775d8dea65b5bb7dd3b3706"
          },
          {
            "file": "frame-08.jpg",
            "index": 648,
            "time_seconds": 10.8,
            "sha256": "996462cbbdb1383c26064a96f9b49699a41ccfa328ebf6ba33ea9b1b5f0eacba"
          },
          {
            "file": "frame-09.jpg",
            "index": 741,
            "time_seconds": 12.35,
            "sha256": "2853da874cc25b2afbdd595653ef4f4b316e5f85b2900a486c2100fd7681b31b"
          },
          {
            "file": "frame-10.jpg",
            "index": 834,
            "time_seconds": 13.9,
            "sha256": "968664272019793bd50e2edf7f44a72d7ef78689946587e716a8d1b8b1142cbc"
          },
          {
            "file": "frame-11.jpg",
            "index": 926,
            "time_seconds": 15.433333,
            "sha256": "388824695676b75063b05988d1a971d7f3d93d3986985b0b29f136a59dd684f9"
          },
          {
            "file": "frame-12.jpg",
            "index": 1019,
            "time_seconds": 16.983333,
            "sha256": "057f0b02300da0489498a78ba402537f13040a2abca6ca60e4099c71bd2c798c"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "d379f55c0563bc94a33be1f6763f6379e2958eaae3a1ab4eb2e5ee3239206e51"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-phase-remainder-spinners",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-phase-remainder-spinners",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-phase-remainder-spinners.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-phase-remainder-spinners/"
    },
    {
      "video": "content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan.mp4",
      "basename": "symmetry-spectrum-path-diamond-wavelength-scan.mp4",
      "slug": "symmetry-spectrum-path-diamond-wavelength-scan",
      "chapter_section": "Wave Propagation and Interference",
      "caption": null,
      "image_description": "Matching colors connect candidate paths to their contributions in the tip-to-tail sum as wavelength decreases",
      "video_link_line": 1226,
      "context_before": [
        {
          "start_line": 1220,
          "end_line": 1220,
          "markdown": "In this limit, waves passing through slits behave as rays. A narrow beam through one opening reaches a small region of the screen, much as a thrown ball does."
        },
        {
          "start_line": 1222,
          "end_line": 1222,
          "markdown": "Each path shares its color with its contribution to the tip-to-tail sum. As the wavelength shrinks, an increasingly narrow range of paths around the straight path carries the sum forward."
        }
      ],
      "context_after": [],
      "sources": [
        "scripts/generate_symmetry_spectrum_path_diamond.py",
        "scripts/generate_symmetry_normalized_path_diamond.py",
        "scripts/generate_symmetry_dense_path_diamond.py",
        "scripts/generate_symmetry_two_tip_to_tail_limits.py"
      ],
      "generation_note": "Main script --render configures the normalized-path renderer with fixed path-position colors, then calls drawing.render() from dense_path_diamond through normalized_path_diamond. These imported modules supply the normalized propagation model, frame schedule, geometry, and general drawing helpers. There is no input video; --check and --encoded-check produce validation.",
      "evidence": "Main NAME matches exactly; configure_renderer installs this movie name and frame implementation in the reused renderer. Matching validation records 20,001 paths, wavelengths 0.5/0.05/0.005, 2160 frames at 60 fps, 36 s, fixed path/color hashes, and unchanged normalized scalar propagation. Matching encoded-validation.json records 1440 x 1080 and confirms all frames.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_spectrum_path_diamond.py",
          "file": "sources/scripts/generate_symmetry_spectrum_path_diamond.py.txt",
          "sha256": "581d06457b5e0baefeace9f540d1e46b6d06b459e60dc250b7394d5776cba13d",
          "bytes": 14370,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_spectrum_path_diamond.py"
        },
        {
          "path": "scripts/generate_symmetry_normalized_path_diamond.py",
          "file": "sources/scripts/generate_symmetry_normalized_path_diamond.py.txt",
          "sha256": "9fc3910cc091e5f3825d7e25636e16ef9f5d412da001dff04f507a4d03572009",
          "bytes": 13465,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_normalized_path_diamond.py"
        },
        {
          "path": "scripts/generate_symmetry_dense_path_diamond.py",
          "file": "sources/scripts/generate_symmetry_dense_path_diamond.py.txt",
          "sha256": "931e8990decd2d1bd9ecffb036d774cd7a634a189e3e7ef41c1cf7b61984ea17",
          "bytes": 18033,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_dense_path_diamond.py"
        },
        {
          "path": "scripts/generate_symmetry_two_tip_to_tail_limits.py",
          "file": "sources/scripts/generate_symmetry_two_tip_to_tail_limits.py.txt",
          "sha256": "9a7e8081dc0333b30ba0d85cd49552af8cc9377a65b2bae582b7d1c5043bae12",
          "bytes": 11631,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_two_tip_to_tail_limits.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan-validation.json",
          "file": "symmetry-spectrum-path-diamond-wavelength-scan/checks/symmetry-spectrum-path-diamond-wavelength-scan-validation.json",
          "sha256": "333ef80306d1c29f5bc95b7f29937150c488f693ed058ec14726eac7a864a2cf",
          "bytes": 1738
        },
        {
          "path": "content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan-encoded-validation.json",
          "file": "symmetry-spectrum-path-diamond-wavelength-scan/checks/symmetry-spectrum-path-diamond-wavelength-scan-encoded-validation.json",
          "sha256": "512843f7eead1c9d266ebf8dd68de97bdda0daa80d96ae210f959bfa425ab11c",
          "bytes": 4034
        }
      ],
      "manifest": "symmetry-spectrum-path-diamond-wavelength-scan/manifest.json",
      "manifest_sha256": "a841a511bec9e0f69c28b45e8bba7916477cb02593989613fb1e703534a1ee1b",
      "frame_directory": "symmetry-spectrum-path-diamond-wavelength-scan/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-spectrum-path-diamond-wavelength-scan.mp4",
        "video_sha256": "20b1b041404d6a38d47cf4b9e638fbb4eaf620ddf22e76f0417e9f335134209f",
        "duration_seconds": 36.0,
        "fps": 60.0,
        "frame_count": 2160,
        "width": 1440,
        "height": 1080,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "421a8fd7d7fe6689d825324b16e2e1bb5c6577c8dd256d1f7f5c07254d2280f6"
          },
          {
            "file": "frame-02.jpg",
            "index": 196,
            "time_seconds": 3.266667,
            "sha256": "7d34facb9db2c4ed7169d043e4712d5d71469e79e55b085ab2182ce36f06d35a"
          },
          {
            "file": "frame-03.jpg",
            "index": 393,
            "time_seconds": 6.55,
            "sha256": "b0eef189539a99da2dfa64018c864f2206bfb86e46c36728c39a81217847b684"
          },
          {
            "file": "frame-04.jpg",
            "index": 589,
            "time_seconds": 9.816667,
            "sha256": "d44e8cd8aa64da02c350f5e9469ec055210a99125c4b578fb0b87ef8a1a660cc"
          },
          {
            "file": "frame-05.jpg",
            "index": 785,
            "time_seconds": 13.083333,
            "sha256": "db3d4912ad03fdc8da2cd3aada1db78810edea836d6cd5eb8dfa1be0ebc0604e"
          },
          {
            "file": "frame-06.jpg",
            "index": 981,
            "time_seconds": 16.35,
            "sha256": "73370613ff47f342f21d1388170b4908186891b64b53b49f9d32d4d7e8a214d1"
          },
          {
            "file": "frame-07.jpg",
            "index": 1178,
            "time_seconds": 19.633333,
            "sha256": "a236370e626b2ba69dbad25e58e13609d8802f2164912afed2ba64aed443cb46"
          },
          {
            "file": "frame-08.jpg",
            "index": 1374,
            "time_seconds": 22.9,
            "sha256": "514045adb4218364883c85c9cc0be10043c2ac0b4d2d64ffeabef86ec0fab770"
          },
          {
            "file": "frame-09.jpg",
            "index": 1570,
            "time_seconds": 26.166667,
            "sha256": "257c6a0e3ca661df8ac556efa21109b65c683ac73c7910dd671da4054964cf22"
          },
          {
            "file": "frame-10.jpg",
            "index": 1766,
            "time_seconds": 29.433333,
            "sha256": "e183cb4f077cc33e66057e10cf8adc2df555dc853338526b663e4c2d69be9c4b"
          },
          {
            "file": "frame-11.jpg",
            "index": 1963,
            "time_seconds": 32.716667,
            "sha256": "963d828c3a271a9dee567a8c2bb07270c53d9fc0a4f2aae507bf808366e1bbbb"
          },
          {
            "file": "frame-12.jpg",
            "index": 2159,
            "time_seconds": 35.983333,
            "sha256": "76d0501c6a867291ab2dda11e7a0ec99247a23eeb751779ded752c2fd6b73dd4"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "ff5c3d14cab4bddc8203ebfe6c7d99cbe8f05a815b67c1243f61f7e1be8bc921"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-spectrum-path-diamond-wavelength-scan",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-spectrum-path-diamond-wavelength-scan",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-spectrum-path-diamond-wavelength-scan.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-spectrum-path-diamond-wavelength-scan/"
    },
    {
      "video": "content/drafts/animations/symmetry-amplitude-probability.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-amplitude-probability.mp4",
      "basename": "symmetry-amplitude-probability.mp4",
      "slug": "symmetry-amplitude-probability",
      "chapter_section": "From Wave Mechanics to Quantum Mechanics",
      "caption": "From intensity to position measurement probability density",
      "image_description": "A complex amplitude and its squared magnitude predict the distribution of repeated position measurements at two wavelengths",
      "video_link_line": 1241,
      "context_before": [
        {
          "start_line": 1231,
          "end_line": 1235,
          "markdown": "```math\n\\rho(x)=|\\psi(x)|^2,\n\\qquad\n\\int_{-\\infty}^{\\infty}\\rho(x)\\,dx=1.\n```"
        },
        {
          "start_line": 1237,
          "end_line": 1237,
          "markdown": "We could write the same relationship in the wave-number representation. In either case, the interpretation is as follows. A state is a superposition of the eigenstates in the chosen basis. An ideal measurement leaves the state in a specific eigenstate of the measured operator. The value of the measurement is the eigenvalue of that state. The squared magnitudes of the coefficients multiplying those eigenstates determine the outcome probabilities. They must sum to one for discrete outcomes. For continuous quantities such as position and wave number, they give probability densities that integrate to one. In this view, we may say, loosely, and only if we are so inclined, that the thing that \"is\" is a wave packet, and the wave-number distribution we measure is given by the squared magnitudes of its Fourier components."
        }
      ],
      "context_after": [
        {
          "start_line": 1245,
          "end_line": 1245,
          "markdown": "To connect our wave description to mechanics, we need to relate phase to action."
        },
        {
          "start_line": 1247,
          "end_line": 1247,
          "markdown": "Action, the quantity extremized by a physically valid path, can be constructed from the structure of **spacetime**, as articulated in the theory of special relativity, which will be the topic of our next chapter. Crudely speaking, because the quantity to be extremized must be agreed upon by all observers, it is natural that it should be an invariant of symmetry actions on spacetime. This leads to the result that the action is, in free motion, for massive bodies, proportional to an invariant built from translations — the time elapsed along a path as measured in a body's rest frame — times a dual invariant built from time and space translation generators. The former quantity is called **proper time** while the latter is the body's **mass**."
        }
      ],
      "sources": [
        "scripts/generate_symmetry_amplitude_probability.py",
        "scripts/ccr2_reels/episodes_quantum.py",
        "scripts/ccr2_reels/core.py"
      ],
      "generation_note": "Main script --render adapts accepted Reel 11 to landscape. It imports detector_state from episodes_quantum and artwork/math helpers from core; neither a portrait MP4 nor a postprocessor is consumed. --verify-video can refresh decoded-video validation without rerendering.",
      "evidence": "Exact STEM/output match. Validation names the normalized paraxial Gaussian model, seeds 73921/73922, wave numbers 8/160, and a video block reporting 840 frames, 30 fps, 1920 x 1080, 28 s. The current MP4 matches that block's SHA-256 (1c8983aa042f447817ec993a7139134c5d2c412565f7d32badf6e8f487886adf).",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_amplitude_probability.py",
          "file": "sources/scripts/generate_symmetry_amplitude_probability.py.txt",
          "sha256": "4bf9b265921515b60eda56c3b8c7726f54b7576c77cab9cd0e2cd7447561e8e7",
          "bytes": 16479,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_amplitude_probability.py"
        },
        {
          "path": "scripts/ccr2_reels/episodes_quantum.py",
          "file": "sources/scripts/ccr2_reels/episodes_quantum.py.txt",
          "sha256": "b0e163a0e6a1a4f9e67da78cc2150c45bf60fec27a7df89221fe2795e2d2abf3",
          "bytes": 17372,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/episodes_quantum.py"
        },
        {
          "path": "scripts/ccr2_reels/core.py",
          "file": "sources/scripts/ccr2_reels/core.py.txt",
          "sha256": "ea6b4f78ba40628d37cacae2b5cac175cd2442b0afa1d35fabdb4d5081384dd8",
          "bytes": 6903,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/core.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-amplitude-probability-validation.json",
          "file": "symmetry-amplitude-probability/checks/symmetry-amplitude-probability-validation.json",
          "sha256": "1c125b794980d0b210d6e3611c61a665f2ec509f7b74e1a3de0835d80d9e6644",
          "bytes": 2831
        }
      ],
      "manifest": "symmetry-amplitude-probability/manifest.json",
      "manifest_sha256": "f64e65afb413f01ff49560f06c00056274e84608a519090b6c4b9bd41c6480ef",
      "frame_directory": "symmetry-amplitude-probability/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-amplitude-probability.mp4",
        "video_sha256": "1c8983aa042f447817ec993a7139134c5d2c412565f7d32badf6e8f487886adf",
        "duration_seconds": 28.0,
        "fps": 30.0,
        "frame_count": 840,
        "width": 1920,
        "height": 1080,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "86b22b603b566030048630635241f9b02ad3a857ad01ad334c796f676b640a27"
          },
          {
            "file": "frame-02.jpg",
            "index": 76,
            "time_seconds": 2.533333,
            "sha256": "cc61ff66510bdb07c70aa063bdc14384769853c11d9e7a2fe07551801d7b2535"
          },
          {
            "file": "frame-03.jpg",
            "index": 153,
            "time_seconds": 5.1,
            "sha256": "62197a74413229c9728ce260e603bcebf300d417a29366a71bf9f3d315a7d9ef"
          },
          {
            "file": "frame-04.jpg",
            "index": 229,
            "time_seconds": 7.633333,
            "sha256": "158541b1c41cd388844755918c3650d21f7c78084fd13b7e6b788aa0b89e6cc8"
          },
          {
            "file": "frame-05.jpg",
            "index": 305,
            "time_seconds": 10.166667,
            "sha256": "ee6fcaba911c9ed33f246ade26398bc1b583f30595279c4c3ab107472efb9bc7"
          },
          {
            "file": "frame-06.jpg",
            "index": 381,
            "time_seconds": 12.7,
            "sha256": "93c053b023ddbe4478f089141f67546406ae5aed6ff4513c5dcd934ffad8e610"
          },
          {
            "file": "frame-07.jpg",
            "index": 458,
            "time_seconds": 15.266667,
            "sha256": "b24b66f59906e477fb35a39b07ee1faf4ec15d3dc98bd80a0c03d769da42fc2b"
          },
          {
            "file": "frame-08.jpg",
            "index": 534,
            "time_seconds": 17.8,
            "sha256": "5f4bc51a3603157f08e4a96ba44cd5a4f2ea8c44304bd82ae736225bd5e35bfb"
          },
          {
            "file": "frame-09.jpg",
            "index": 610,
            "time_seconds": 20.333333,
            "sha256": "3377b52242520742fc0719f3d6991ca61f158ae2d00ce59879792692f497d1b4"
          },
          {
            "file": "frame-10.jpg",
            "index": 686,
            "time_seconds": 22.866667,
            "sha256": "6247d84b35bebd2f1b34668379fa7eab778b41dcb95627593c9ad548145be9e4"
          },
          {
            "file": "frame-11.jpg",
            "index": 763,
            "time_seconds": 25.433333,
            "sha256": "e5e833cae789916bc16151ab046edce8ff8ae607cf5f151d84e8b8a839a6abf6"
          },
          {
            "file": "frame-12.jpg",
            "index": 839,
            "time_seconds": 27.966667,
            "sha256": "af9ef60ca519b1dcb0ba39599d8f69485a839efe0f562e62fc4eadb2fd64a88a"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "8caf4ad2b68e98474c3f2edabb96f5aa32ecab9127c95abb890e35d7c3aff6d1"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-amplitude-probability",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-amplitude-probability",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-amplitude-probability.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-amplitude-probability/"
    },
    {
      "video": "content/drafts/animations/symmetry-collision-shared-scale.mp4",
      "original_reference": "animations/symmetry-collision-shared-scale.mp4",
      "basename": "symmetry-collision-shared-scale.mp4",
      "slug": "symmetry-collision-shared-scale",
      "chapter_section": "From Wave Mechanics to Quantum Mechanics",
      "caption": "A shared conversion factor makes phase and action select the same collision",
      "image_description": "Varying a candidate collision point leaves phase and action stationary at different points for unequal conversion factors, and at the same point for a shared factor",
      "video_link_line": 1287,
      "context_before": [
        {
          "start_line": 1276,
          "end_line": 1281,
          "markdown": "```math\n\\begin{aligned}\n\\delta\\phi_1&=\\epsilon,\\qquad\\delta\\phi_2=-\\epsilon,\\\\\n\\delta S&=a_1\\delta\\phi_1+a_2\\delta\\phi_2=(a_1-a_2)\\epsilon.\n\\end{aligned}\n```"
        },
        {
          "start_line": 1283,
          "end_line": 1283,
          "markdown": "If the collision changes a particle’s speed or direction, we can shift the candidate collision point so that its phase changes to first order. Choose such a shift, so \\(\\epsilon\\ne0\\). For the action to be stationary at the same point, $a_1=a_2$. The shared conversion factor is $\\hbar$."
        }
      ],
      "context_after": [
        {
          "start_line": 1291,
          "end_line": 1291,
          "markdown": "We can measure its numerical value in the lab by comparing mass, as measured through collisions, to wavelength, as measured through interference. We then find:"
        },
        {
          "start_line": 1293,
          "end_line": 1295,
          "markdown": "```math\n\\frac{S}{\\phi}:=\\hbar\\approx 1.055\\times10^{-34}\\,\\mathrm{J\\,s}.\n```"
        }
      ],
      "sources": [
        "scripts/generate_symmetry_collision_shared_scale.py"
      ],
      "generation_note": "Main script --render first writes review stills and numerical validation, then directly encodes the shared-scale collision illustration and appends decoded-video metadata to its validation report. It has no local Python imports or input movie.",
      "evidence": "The script's STEM and output match the linked movie. Matching validation JSON records 1600 x 900, 24 fps, 720 frames, 30 s, and confirms those values in its encoded_video block. It contains no movie hash, so this is source/output-name attribution rather than a hash-attested build.",
      "source_files": [
        {
          "path": "scripts/generate_symmetry_collision_shared_scale.py",
          "file": "sources/scripts/generate_symmetry_collision_shared_scale.py.txt",
          "sha256": "2baf2763d05bfd3654c9bb9202dc7a0454f3ca6d4fe965079a87c42c06918868",
          "bytes": 21743,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/generate_symmetry_collision_shared_scale.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-collision-shared-scale-validation.json",
          "file": "symmetry-collision-shared-scale/checks/symmetry-collision-shared-scale-validation.json",
          "sha256": "45a7200089400e0d39c1743ecc3c88c74479f8aa59baa5586263ddf6830e855f",
          "bytes": 1829
        }
      ],
      "manifest": "symmetry-collision-shared-scale/manifest.json",
      "manifest_sha256": "363c4241ee7a8819bb1337714f42315ac8e4c08f714cd27bf7d3210c83f51fc4",
      "frame_directory": "symmetry-collision-shared-scale/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-collision-shared-scale.mp4",
        "video_sha256": "643b2fb49af55e498bcfecd3f8d610c91b4697d58a2683bf0de2579752b3f55a",
        "duration_seconds": 30.0,
        "fps": 24.0,
        "frame_count": 720,
        "width": 1600,
        "height": 900,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "6d81af59e3c7ecaf74083149d0daaa278f228c7af77e35cbabff08b7ee709e69"
          },
          {
            "file": "frame-02.jpg",
            "index": 65,
            "time_seconds": 2.708333,
            "sha256": "96d0629519877a37482c6f2d82a451284891f57bbd15120d3fba23c9adb8eda0"
          },
          {
            "file": "frame-03.jpg",
            "index": 131,
            "time_seconds": 5.458333,
            "sha256": "e084c968c0de3a1efcd816982d84eedb0555932af7355c38453bc78ddb09463c"
          },
          {
            "file": "frame-04.jpg",
            "index": 196,
            "time_seconds": 8.166667,
            "sha256": "9db6c70536c4174cc854c1641719f61bbe37b63803bbc1693e5bd42f3281ff12"
          },
          {
            "file": "frame-05.jpg",
            "index": 261,
            "time_seconds": 10.875,
            "sha256": "f08284f1bacdf747a92bf44a612001213d42fd8bcdca431a944725f1e4074b27"
          },
          {
            "file": "frame-06.jpg",
            "index": 327,
            "time_seconds": 13.625,
            "sha256": "647aa263ea2346c8832995453f9430353db5db1f7558692b874423703db46681"
          },
          {
            "file": "frame-07.jpg",
            "index": 392,
            "time_seconds": 16.333333,
            "sha256": "9d2e198cb5d684e3425a87020fe7b945caba6eb2fb5a77d319f56f06535a9cbc"
          },
          {
            "file": "frame-08.jpg",
            "index": 458,
            "time_seconds": 19.083333,
            "sha256": "4076854070a4700118f830df5ec0b23f62c2041ecfc611601abe118567d84b87"
          },
          {
            "file": "frame-09.jpg",
            "index": 523,
            "time_seconds": 21.791667,
            "sha256": "4e90cf9534bb4451feb5730b05eb4b5fd3a24b2beb1655f5db1e15da39142d25"
          },
          {
            "file": "frame-10.jpg",
            "index": 588,
            "time_seconds": 24.5,
            "sha256": "d2a2faab2d944e7087395d836b97297531f1a7d69abf58c28b0a40264cb4e779"
          },
          {
            "file": "frame-11.jpg",
            "index": 654,
            "time_seconds": 27.25,
            "sha256": "6fc116eb804d3caa9d2adb69f7a75d37022ed52c6793c507ff269cb7b1b6a1e2"
          },
          {
            "file": "frame-12.jpg",
            "index": 719,
            "time_seconds": 29.958333,
            "sha256": "609f84c385a1944d68d18a349caeb22b82aa594395739dfb42c2426807630fff"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "86c31ac6be1120a0dee56ff9e19b05c95859d67804cd761b565b8908a7b725cb"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-collision-shared-scale",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-collision-shared-scale",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-collision-shared-scale.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-collision-shared-scale/"
    },
    {
      "video": "content/drafts/animations/symmetry-action-phase-desktop.mp4",
      "original_reference": "../../content/drafts/animations/symmetry-action-phase-desktop.mp4",
      "basename": "symmetry-action-phase-desktop.mp4",
      "slug": "symmetry-action-phase-desktop",
      "chapter_section": "From Wave Mechanics to Quantum Mechanics",
      "caption": null,
      "image_description": "Three wave-number shells share the same proper-time interval while phase cycles and action accumulates at corresponding rates",
      "video_link_line": 1305,
      "context_before": [
        {
          "start_line": 1297,
          "end_line": 1297,
          "markdown": "The smaller $\\hbar$ is, the more phase cycles a given action difference produces. For candidate paths, we use the wave mode appropriate to each segment and add the accumulated phases, following the procedure described earlier. For two candidate paths:"
        },
        {
          "start_line": 1299,
          "end_line": 1301,
          "markdown": "```math\n\\Delta\\phi=\\frac{\\Delta S}{\\hbar}.\n```"
        }
      ],
      "context_after": [
        {
          "start_line": 1307,
          "end_line": 1307,
          "markdown": "But this is exactly our condition for the path sum to be dominated by stationary paths. For everyday bodies, $\\hbar$ is tiny compared with action differences so that their motion is effectively deterministic. Are we saying that the laws of motion we learn in high school physics are an approximation? Yes, a very good approximation."
        },
        {
          "start_line": 1309,
          "end_line": 1309,
          "markdown": "We can now express our $H_3$ commutation relation in units of action:"
        }
      ],
      "sources": [
        "scripts/ccr2_reels/render_action_phase_desktop.py",
        "scripts/ccr2_reels/revise_action_phase.py",
        "scripts/ccr2_reels/core.py",
        "scripts/ccr2_reels/render.py",
        "scripts/ccr2_reels/retitle_live.py",
        "scripts/ccr2_reels/validate_retime.py",
        "scripts/ccr2_reels/retime.py"
      ],
      "generation_note": "Run render_action_phase_desktop.py --render. It directly encodes landscape frames using the shared state and timing constants from revise_action_phase and drawing primitives from core. The remaining listed modules are transitive imports through revise_action_phase/retitle_live; their portrait packaging and retiming entrypoints are not executed by the desktop build, and no earlier movie is consumed.",
      "evidence": "Exact STEM and output match. Matching validation explicitly identifies shared_state=revise_action_phase.state and records masses 1/2/3 eV, proper-time interval 0..12 fs, 1008 frames at 42 fps, 1920 x 1080, 24 s. It verifies shell/proper-time/phase/action invariants but contains no MP4 hash, so source attribution is not a hash-attested build.",
      "source_files": [
        {
          "path": "scripts/ccr2_reels/render_action_phase_desktop.py",
          "file": "sources/scripts/ccr2_reels/render_action_phase_desktop.py.txt",
          "sha256": "17c442c4f187fa48779bf7e8e7d5df42ca77f25f53062c0e9d82e872a3305130",
          "bytes": 11739,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/render_action_phase_desktop.py"
        },
        {
          "path": "scripts/ccr2_reels/revise_action_phase.py",
          "file": "sources/scripts/ccr2_reels/revise_action_phase.py.txt",
          "sha256": "c771209296928a17809d5f0d9789f0774af363b47949589a1fb0ed694c117664",
          "bytes": 10807,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/revise_action_phase.py"
        },
        {
          "path": "scripts/ccr2_reels/core.py",
          "file": "sources/scripts/ccr2_reels/core.py.txt",
          "sha256": "ea6b4f78ba40628d37cacae2b5cac175cd2442b0afa1d35fabdb4d5081384dd8",
          "bytes": 6903,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/core.py"
        },
        {
          "path": "scripts/ccr2_reels/render.py",
          "file": "sources/scripts/ccr2_reels/render.py.txt",
          "sha256": "a76fb42cc469a255b42a87307dbc5a95e6acc84ac89ea42159599e88afa6e573",
          "bytes": 19296,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/render.py"
        },
        {
          "path": "scripts/ccr2_reels/retitle_live.py",
          "file": "sources/scripts/ccr2_reels/retitle_live.py.txt",
          "sha256": "f72b785c1e1d1b84309cbb397267d9dc4586711562c3d1b60e5ce28bc2b4aaf2",
          "bytes": 15720,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/retitle_live.py"
        },
        {
          "path": "scripts/ccr2_reels/validate_retime.py",
          "file": "sources/scripts/ccr2_reels/validate_retime.py.txt",
          "sha256": "14dbc097067c5ddabf95bddb18cdf402ff9dee5e96a89d201c28825a27a27864",
          "bytes": 5251,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/validate_retime.py"
        },
        {
          "path": "scripts/ccr2_reels/retime.py",
          "file": "sources/scripts/ccr2_reels/retime.py.txt",
          "sha256": "18e2d96a281771c7ca0219525476a6f2042200e04af074b14326a7ea9619fe42",
          "bytes": 1967,
          "matches_git_head": true,
          "github_url": "https://github.com/wadelamble/constrained-improvisation/blob/974f9de352542963116af37c417ad68cd3a2c101/scripts/ccr2_reels/retime.py"
        }
      ],
      "generator_checks": [
        {
          "path": "content/drafts/animations/symmetry-action-phase-desktop-validation.json",
          "file": "symmetry-action-phase-desktop/checks/symmetry-action-phase-desktop-validation.json",
          "sha256": "a6c7567b764d7ff28dd272c8ee36b54cb8a27866b309f063755d335883456345",
          "bytes": 653
        }
      ],
      "manifest": "symmetry-action-phase-desktop/manifest.json",
      "manifest_sha256": "2210106e5e7a87553b9839025c2cea10272379a627bcde64014f743456689567",
      "frame_directory": "symmetry-action-phase-desktop/frames/",
      "media": {
        "schema_version": 1,
        "recipe": "encoded-frames-v1-12-uniform-inclusive-native-resolution",
        "video": "content/drafts/animations/symmetry-action-phase-desktop.mp4",
        "video_sha256": "dcbd69a9b39fa15cc4b082395a1fb799acc1877360429143fa18c997984a6492",
        "duration_seconds": 24.0,
        "fps": 42.0,
        "frame_count": 1008,
        "width": 1920,
        "height": 1080,
        "method": "Twelve evenly spaced decoded frame indices, including first and last. Native-resolution JPEGs from the encoded MP4; timestamps read from FFmpeg showinfo. No source rerendering. Frame indices are zero-based. Sparse samples do not establish continuous motion or capture every transition.",
        "frames": [
          {
            "file": "frame-01.jpg",
            "index": 0,
            "time_seconds": 0.0,
            "sha256": "dd2852fd5a1f02e77fd3d4e31a92f5596fd7d23f94c74b79e605ba321e537e00"
          },
          {
            "file": "frame-02.jpg",
            "index": 92,
            "time_seconds": 2.190476,
            "sha256": "53a590ddad3137a1c7e284a2fe1e1f561f3f0395b9be355456afe44694bcc157"
          },
          {
            "file": "frame-03.jpg",
            "index": 183,
            "time_seconds": 4.357143,
            "sha256": "f56a39a6d8aa06f7834b40c89facf26e954bc593ac217b095b031a75d98e3c59"
          },
          {
            "file": "frame-04.jpg",
            "index": 275,
            "time_seconds": 6.547619,
            "sha256": "d547265bbbdcb8007c5287377b5ab39df2a209a7cdb57da03cbc18e0580dfd75"
          },
          {
            "file": "frame-05.jpg",
            "index": 366,
            "time_seconds": 8.714286,
            "sha256": "6748fd03c7ae3c1934019a4f261626ff4d2eeea81b5c5e215581b50a50a88168"
          },
          {
            "file": "frame-06.jpg",
            "index": 458,
            "time_seconds": 10.904762,
            "sha256": "69db5fcd611a4f640b71acc4b4f3639c7e67f41034ca70d32ee60f2a67767a7a"
          },
          {
            "file": "frame-07.jpg",
            "index": 549,
            "time_seconds": 13.071429,
            "sha256": "c54a172932503d53795fb0d78c1fed097ff5421955ae7e89d83408858cf1cca1"
          },
          {
            "file": "frame-08.jpg",
            "index": 641,
            "time_seconds": 15.261905,
            "sha256": "f920b419d173ce3175de59e745657956cbf5583d36f472abaf13641cef198bb5"
          },
          {
            "file": "frame-09.jpg",
            "index": 732,
            "time_seconds": 17.428571,
            "sha256": "a81626247842bc8aec8778ac7f2282d27115e36c6577487bbae7ed2aa59ca055"
          },
          {
            "file": "frame-10.jpg",
            "index": 824,
            "time_seconds": 19.619048,
            "sha256": "9a67606d6e3ecdbed5a474e85b0cde032968673c302426161866284d6af2c974"
          },
          {
            "file": "frame-11.jpg",
            "index": 915,
            "time_seconds": 21.785714,
            "sha256": "cb17c985d49bba3383ab31dae272fde2488cf248a21dd05347acea2b45775d22"
          },
          {
            "file": "frame-12.jpg",
            "index": 1007,
            "time_seconds": 23.97619,
            "sha256": "14162a1ea4fb63c61cf8e841347841f1f0cad89343d5d8128c7666fee8c00988"
          }
        ],
        "contact_sheet": {
          "file": "contact-sheet.jpg",
          "sha256": "4457c8f52086ce7415ab7b6d51a0de743b9bfc0c2fc9f46c09e89b6a23785c2e"
        }
      },
      "chapter_url": "/constrained-improvisation/symmetry/#animation-symmetry-action-phase-desktop",
      "public_chapter_url": "https://wadelamble.github.io/constrained-improvisation/symmetry/#animation-symmetry-action-phase-desktop",
      "public_video_url": "https://wadelamble.github.io/constrained-improvisation/assets/animations/symmetry-action-phase-desktop.mp4",
      "review_url": "/constrained-improvisation/animation-review/symmetry-action-phase-desktop/"
    }
  ]
}
