@autoaiai/aicut 0.1.7 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANUAL.md +94 -84
- package/package.json +1 -1
- package/product.json +2 -1
- package/skills/aicut/SKILL.md +4 -4
- package/skills/aicut/references/commands.md +15 -15
- package/skills/aicut/references/film-format.md +1 -1
- package/skills/aicut/references/recipes.md +4 -4
- package/skills/aicut/references/troubleshooting.md +2 -2
- package/skills/aicut-motion/SKILL.md +75 -9
- package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
- package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
- package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
- package/skills/aicut-motion/references/design-systems/README.md +1 -1
- package/skills/aicut-motion/references/motion-delivery.md +55 -0
- package/skills/aicut-motion/references/pages.md +1 -1
- package/skills/aicut-motion/references/style-index.json +690 -0
- package/skills/aicut-motion/references/style-research.md +73 -0
- package/skills/aicut-motion/references/style-selection.md +71 -0
- package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
- package/skills/aicut-motion/scripts/atlas.mjs +109 -0
- package/studio/client.mjs +3 -3
- package/studio/server/fetch-github.mjs +14 -3
- package/studio/server/server.mjs +71 -6
- package/studio/server/skill-summaries.mjs +60 -0
- package/studio/server/skills.mjs +121 -7
- package/studio/server/styles-store.mjs +235 -0
- package/studio/server/template-posters.mjs +2 -2
- package/studio/server/templates-page.mjs +93 -93
- package/studio/server/templates.mjs +32 -7
- package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
- package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
- package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
- package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
- package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
- package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
- package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
- package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
- package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
- package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
- package/studio/ui/dist/index.html +4 -3
- package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
- package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
- package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
- package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
- package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
- package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
- package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
- package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 85,
|
|
3
|
+
"name": "MacPaint 1984",
|
|
4
|
+
"nameZh": "MacPaint 1984 画板",
|
|
5
|
+
"family": "Retro Digital",
|
|
6
|
+
"familyZh": "复古数字",
|
|
7
|
+
"description": "Pure 1-bit black & white at the original 72-dpi grain: real Atkinson-dithered grayscale (blown-out highlights, crunchy shadows), 8x8 fill patterns, striped title bars, marching ants and bitmap type.",
|
|
8
|
+
"motionNotes": "Motion: Built the way a 1984 user would: the dithered image scans in row by row, then pencil, line, bucket, rectangle and text tools each switch on in the palette and lay down their marks in snappy, unanimated 1-bit steps, ending on crawling marching ants.",
|
|
9
|
+
"explainerNotes": "Explainer: Explains with the 1984 desktop itself: an alert asks the question and, once OK is clicked, zoom-rects turn it into the window title; labelled Finder icons are dragged into a Model icon while the item count climbs; a Model window cycles Guess/Check/Adjust buttons with a stop alert and menu-bar beep flash, dashed error arrow and Control-Panel weight sliders; a pattern-filled chart climbs to 97% inside marching ants.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "mac-1bit",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Multi-scale Truchet (Carlson "winged" tiles): each tile = square in its fill colour, quarter-
|
|
3
|
+
// annulus bands (r = s/3..2s/3) about two opposite corners, plus corner "wing" discs (r = s/3)
|
|
4
|
+
// that spill over the tile edge. Child tiles invert colours, so paths stay continuous across
|
|
5
|
+
// scales. The hidden message is carried by colour: closed regions are flood-labelled and those
|
|
6
|
+
// lying inside the letters "AI" are printed in a third ink (with a touch of screen-print
|
|
7
|
+
// misregistration).
|
|
8
|
+
const TAU = Math.PI * 2;
|
|
9
|
+
const C_DARK = [20, 30, 58], C_LIGHT = [242, 233, 212], C_ACC = [232, 86, 44];
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// =====================================================================================
|
|
13
|
+
// EXPLAINER — the model is a Truchet maze: an input enters on the left, the path its band
|
|
14
|
+
// takes decides which output it reaches. Training = flipping tiles (the weights) so the
|
|
15
|
+
// path routes to the right answer. Scene changes re-lay the tiles in a diagonal sweep.
|
|
16
|
+
// =====================================================================================
|
|
17
|
+
const NAVY = `rgb(${C_DARK})`, CREAM = `rgb(${C_LIGHT})`, ACC = `rgb(${C_ACC})`;
|
|
18
|
+
const XS = (t, a, b) => H.clamp((t - a) / (b - a));
|
|
19
|
+
const EXIT = [{ L: 'T', T: 'L', R: 'B', B: 'R' }, { L: 'B', B: 'L', T: 'R', R: 'T' }];
|
|
20
|
+
const MOVE = { R: [1, 0, 'L'], L: [-1, 0, 'R'], T: [0, -1, 'B'], B: [0, 1, 'T'] };
|
|
21
|
+
const MID = { L: [0, 0.5], R: [1, 0.5], T: [0.5, 0], B: [0.5, 1] };
|
|
22
|
+
const CORNER = { LT: [0, 0], TL: [0, 0], RB: [1, 1], BR: [1, 1], LB: [0, 1], BL: [0, 1], TR: [1, 0], RT: [1, 0] };
|
|
23
|
+
const MC = 9, MR = 5, MS = 80, MX0 = 300, MY0 = 150, R_IN = 2, R_CAT = 1, R_DOG = 3;
|
|
24
|
+
function trace(o, r0) {
|
|
25
|
+
const segs = []; let i = 0, j = r0, e = 'L';
|
|
26
|
+
for (let n = 0; n < 200; n++) {
|
|
27
|
+
if (i < 0 || j < 0 || i >= MC || j >= MR) break;
|
|
28
|
+
const x = EXIT[o[j * MC + i]][e]; segs.push({ i, j, a: e, b: x });
|
|
29
|
+
const [di, dj, ne] = MOVE[x]; i += di; j += dj; e = ne;
|
|
30
|
+
}
|
|
31
|
+
const last = segs[segs.length - 1];
|
|
32
|
+
return { segs, out: last.b === 'R' && last.i === MC - 1 ? last.j : -1 };
|
|
33
|
+
}
|
|
34
|
+
let MAZE = null; // deterministic search: a maze that routes to "dog", fixable by flipping tiles to "cat"
|
|
35
|
+
function maze() {
|
|
36
|
+
if (MAZE) return MAZE;
|
|
37
|
+
for (let s = 1; s < 20000 && !MAZE; s++) {
|
|
38
|
+
const r = H.rng(s * 7 + 3), o = new Uint8Array(MC * MR);
|
|
39
|
+
for (let k = 0; k < o.length; k++) o[k] = r() < 0.5 ? 0 : 1;
|
|
40
|
+
const A = trace(o, R_IN);
|
|
41
|
+
if (A.out !== R_DOG || A.segs.length < 11) continue;
|
|
42
|
+
const cand = A.segs.map((g) => g.j * MC + g.i);
|
|
43
|
+
for (let a = 0; a < cand.length && !MAZE; a++) for (let b = a + 1; b < cand.length && !MAZE; b++) {
|
|
44
|
+
if (cand[a] === cand[b]) continue;
|
|
45
|
+
const o2 = o.slice(); o2[cand[a]] ^= 1; o2[cand[b]] ^= 1;
|
|
46
|
+
const B = trace(o2, R_IN);
|
|
47
|
+
if (B.out === R_CAT && B.segs.length >= 11) MAZE = { o, o2, A, B, flips: [cand[a], cand[b]] };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return MAZE;
|
|
51
|
+
}
|
|
52
|
+
function tileXY(k) { return [MX0 + (k % MC) * MS, MY0 + Math.floor(k / MC) * MS]; }
|
|
53
|
+
function drawTile(c, x, y, s, o, fill, band, rot = 0) {
|
|
54
|
+
c.save();
|
|
55
|
+
if (rot) { c.translate(x + s / 2, y + s / 2); c.rotate(rot); c.translate(-x - s / 2, -y - s / 2); }
|
|
56
|
+
if (fill) { c.fillStyle = fill; c.fillRect(x, y, s, s); } // null fill: the block underneath was inked in one pass
|
|
57
|
+
c.strokeStyle = band; c.lineWidth = s / 3; c.lineCap = 'butt'; c.beginPath();
|
|
58
|
+
if (o === 0) { c.arc(x, y, s / 2, 0, Math.PI / 2); c.moveTo(x + s / 2, y + s); c.arc(x + s, y + s, s / 2, Math.PI, Math.PI * 1.5); }
|
|
59
|
+
else { c.moveTo(x + s, y + s / 2); c.arc(x + s, y, s / 2, Math.PI / 2, Math.PI); c.moveTo(x + s / 2, y + s); c.arc(x, y + s, s / 2, -Math.PI / 2, 0); }
|
|
60
|
+
c.stroke(); c.restore();
|
|
61
|
+
}
|
|
62
|
+
// draw the first `prog` segments of a path as an ink band (fractional last one)
|
|
63
|
+
function flowPath(c, segs, prog, color, s = MS, reverse = false) {
|
|
64
|
+
c.save(); c.strokeStyle = color; c.lineWidth = s / 3 + 1; c.lineCap = 'butt';
|
|
65
|
+
const n = Math.min(segs.length, Math.ceil(prog));
|
|
66
|
+
for (let q = 0; q < n; q++) {
|
|
67
|
+
const g = segs[reverse ? segs.length - 1 - q : q], f = Math.min(1, prog - q);
|
|
68
|
+
const [cx, cy] = CORNER[g.a + g.b], x = MX0 + g.i * s, y = MY0 + g.j * s;
|
|
69
|
+
const ea = reverse ? g.b : g.a, eb = reverse ? g.a : g.b;
|
|
70
|
+
const a0 = Math.atan2(MID[ea][1] - cy, MID[ea][0] - cx), a1r = Math.atan2(MID[eb][1] - cy, MID[eb][0] - cx);
|
|
71
|
+
let d = a1r - a0; while (d > Math.PI) d -= Math.PI * 2; while (d < -Math.PI) d += Math.PI * 2;
|
|
72
|
+
c.beginPath(); c.arc(x + cx * s, y + cy * s, s / 2, a0, a0 + d * f, d < 0); c.stroke();
|
|
73
|
+
}
|
|
74
|
+
c.restore();
|
|
75
|
+
}
|
|
76
|
+
function pointOn(segs, prog, s = MS) { // position along path (prog in segments)
|
|
77
|
+
const q = Math.min(segs.length - 1, Math.max(0, Math.floor(prog))), f = H.clamp(prog - q), g = segs[q];
|
|
78
|
+
const [cx, cy] = CORNER[g.a + g.b];
|
|
79
|
+
const a0 = Math.atan2(MID[g.a][1] - cy, MID[g.a][0] - cx), a1 = Math.atan2(MID[g.b][1] - cy, MID[g.b][0] - cx);
|
|
80
|
+
let d = a1 - a0; while (d > Math.PI) d -= Math.PI * 2; while (d < -Math.PI) d += Math.PI * 2;
|
|
81
|
+
const a = a0 + d * f; return [MX0 + g.i * s + cx * s + Math.cos(a) * s / 2, MY0 + g.j * s + cy * s + Math.sin(a) * s / 2];
|
|
82
|
+
}
|
|
83
|
+
// printed label: navy (or accent) block, cream Rubik Mono, slight accent misregistration
|
|
84
|
+
function plabel(c, text, x, y, o = {}) {
|
|
85
|
+
c.save(); c.font = `400 ${o.size || 18}px "Rubik Mono One"`; c.textBaseline = 'middle';
|
|
86
|
+
const w = c.measureText(text).width + (o.size || 18) * 1.2, h = (o.size || 18) * 1.9;
|
|
87
|
+
const ox = o.align === 'center' ? x - w / 2 : o.align === 'right' ? x - w : x;
|
|
88
|
+
c.fillStyle = ACC; c.fillRect(ox + 3, y - h / 2 + 3, w, h);
|
|
89
|
+
c.fillStyle = o.bg || NAVY; c.fillRect(ox, y - h / 2, w, h);
|
|
90
|
+
c.fillStyle = o.fg || CREAM; c.fillText(text, ox + (o.size || 18) * 0.6, y + 1);
|
|
91
|
+
c.restore(); return w;
|
|
92
|
+
}
|
|
93
|
+
function pet(c, kind, cx, cy, s, ink, bg) {
|
|
94
|
+
c.save(); c.fillStyle = ink;
|
|
95
|
+
c.beginPath(); c.arc(cx, cy + s * 0.08, s * 0.3, 0, Math.PI * 2); c.fill();
|
|
96
|
+
if (kind === 'cat') { for (const k of [-1, 1]) { c.beginPath(); c.moveTo(cx + k * s * 0.08, cy - s * 0.12); c.lineTo(cx + k * s * 0.3, cy - s * 0.34); c.lineTo(cx + k * s * 0.3, cy - s * 0.02); c.fill(); } }
|
|
97
|
+
else { for (const k of [-1, 1]) { c.beginPath(); c.ellipse(cx + k * s * 0.3, cy + s * 0.08, s * 0.1, s * 0.22, k * 0.3, 0, Math.PI * 2); c.fill(); } c.beginPath(); c.ellipse(cx, cy + s * 0.26, s * 0.15, s * 0.11, 0, 0, Math.PI * 2); c.fill(); }
|
|
98
|
+
c.fillStyle = bg; for (const k of [-1, 1]) { c.beginPath(); c.arc(cx + k * s * 0.11, cy + s * 0.04, s * 0.045, 0, Math.PI * 2); c.fill(); }
|
|
99
|
+
c.restore();
|
|
100
|
+
}
|
|
101
|
+
function exampleTile(c, kind, x, y, s, label) {
|
|
102
|
+
c.save(); c.fillStyle = ACC; c.fillRect(x + 3, y + 3, s, s); c.fillStyle = CREAM; c.fillRect(x, y, s, s);
|
|
103
|
+
c.strokeStyle = NAVY; c.lineWidth = 3; c.strokeRect(x, y, s, s); pet(c, kind, x + s / 2, y + s / 2, s, NAVY, CREAM); c.restore();
|
|
104
|
+
if (label) plabel(c, label, x + s / 2, y + s + 24, { size: 26, align: 'center', bg: ACC });
|
|
105
|
+
}
|
|
106
|
+
function arrow(c, x0, y0, x1, y1, p, lw = 5, col = NAVY) {
|
|
107
|
+
if (p <= 0) return; const x = x0 + (x1 - x0) * p, y = y0 + (y1 - y0) * p, a = Math.atan2(y1 - y0, x1 - x0), hd = lw * 3.4;
|
|
108
|
+
c.save(); c.strokeStyle = col; c.fillStyle = col; c.lineWidth = lw; c.beginPath(); c.moveTo(x0, y0); c.lineTo(x - Math.cos(a) * hd * 0.7, y - Math.sin(a) * hd * 0.7); c.stroke();
|
|
109
|
+
c.beginPath(); c.moveTo(x, y); c.lineTo(x - Math.cos(a - 0.55) * hd, y - Math.sin(a - 0.55) * hd); c.lineTo(x - Math.cos(a + 0.55) * hd, y - Math.sin(a + 0.55) * hd); c.closePath(); c.fill(); c.restore();
|
|
110
|
+
}
|
|
111
|
+
function paper(c, W, H_) { c.fillStyle = CREAM; c.fillRect(0, 0, W, H_); }
|
|
112
|
+
// screen-print finish for the explainer's flat scenes: the still's ink-film unevenness (low-frequency
|
|
113
|
+
// density mottle + fine speckle), built once and multiplied over the scene, plus the still's soft vignette.
|
|
114
|
+
let INKF = null;
|
|
115
|
+
function inkFilm(W, H_) {
|
|
116
|
+
if (INKF && INKF.width === W && INKF.height === H_) return INKF;
|
|
117
|
+
const L = H.layer(W, H_), c = L.getContext('2d'), img = c.createImageData(W, H_), d = img.data;
|
|
118
|
+
const nz = H.makeNoise(17), rr = H.rng(3);
|
|
119
|
+
for (let y = 0; y < H_; y++) for (let x = 0; x < W; x++) {
|
|
120
|
+
const v = 0.99 + nz.n2(x * 0.02, y * 0.02) * 0.03 + nz.n2(x * 0.005 + 3.1, y * 0.005) * 0.01 + (rr() - 0.5) * 0.03;
|
|
121
|
+
const o = (y * W + x) * 4, g = Math.round(255 * Math.min(1, v));
|
|
122
|
+
d[o] = g; d[o + 1] = g; d[o + 2] = g; d[o + 3] = 255;
|
|
123
|
+
}
|
|
124
|
+
c.putImageData(img, 0, 0);
|
|
125
|
+
return (INKF = L);
|
|
126
|
+
}
|
|
127
|
+
function finish(c, W, H_) {
|
|
128
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.drawImage(inkFilm(W, H_), 0, 0); c.restore();
|
|
129
|
+
H.vignette(c, W, H_, 0.12);
|
|
130
|
+
}
|
|
131
|
+
const header = (c) => plabel(c, 'HOW DOES AI LEARN?', 24, 44, { size: 28 });
|
|
132
|
+
let STILL = null;
|
|
133
|
+
function still(W, H_) {
|
|
134
|
+
if (!STILL) { STILL = H.layer(W, H_); const st = window.STYLES.find((s) => s.id === 'truchet'); H.render(st, STILL, 4.9); }
|
|
135
|
+
return STILL;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---------- scenes ----------
|
|
139
|
+
function sHook(c, W, H_, t) {
|
|
140
|
+
const z = 1.12 - 0.1 * H.ease.out(XS(t, 0, 2.4)); // slow pull-back off the tiling
|
|
141
|
+
c.save(); c.translate(W / 2, H_ / 2); c.scale(z, z); c.translate(-W / 2, -H_ / 2); c.drawImage(still(W, H_), 0, 0); c.restore();
|
|
142
|
+
c.fillStyle = 'rgba(242,233,212,0.35)'; c.fillRect(0, 0, W, H_);
|
|
143
|
+
const k = H.ease.inOut(XS(t, 1.7, 2.3));
|
|
144
|
+
if (k < 1) {
|
|
145
|
+
const pop = H.ease.back(XS(t, 0.15, 0.55));
|
|
146
|
+
c.save(); c.translate(W / 2 + (24 - W / 2) * k, 360 + (44 - 360) * k); c.scale(pop * (1 - (1 - 28 / 46) * k), pop * (1 - (1 - 28 / 46) * k));
|
|
147
|
+
plabel(c, 'HOW DOES AI LEARN?', 0, 0, { size: 46, align: k > 0.5 ? 'left' : 'center' }); c.restore();
|
|
148
|
+
} else header(c);
|
|
149
|
+
}
|
|
150
|
+
function sData(c, W, H_, t) {
|
|
151
|
+
paper(c, W, H_);
|
|
152
|
+
// the model: a block of tiles that keep flipping as examples pour in
|
|
153
|
+
const r = H.rng(55), MXb = 830, MYb = 150, S = 64;
|
|
154
|
+
c.fillStyle = NAVY; c.fillRect(MXb, MYb, 6 * S, 6 * S);
|
|
155
|
+
const turning = [];
|
|
156
|
+
for (let k = 0; k < 36; k++) {
|
|
157
|
+
const o0 = r() < 0.5 ? 0 : 1, tf = 2.8 + r() * 3.2, x = MXb + (k % 6) * S, y = MYb + Math.floor(k / 6) * S;
|
|
158
|
+
const rot = t > tf ? Math.min(1, (t - tf) / 0.15) * Math.PI / 2 : 0;
|
|
159
|
+
if (rot > 0 && rot < Math.PI / 2) turning.push([x, y, o0, rot]);
|
|
160
|
+
else drawTile(c, x, y, S, rot >= Math.PI / 2 ? o0 ^ 1 : o0, null, CREAM, 0);
|
|
161
|
+
}
|
|
162
|
+
// tiles mid quarter-turn: their empty socket prints as a deeper navy
|
|
163
|
+
for (const [x, y, o0, rot] of turning) {
|
|
164
|
+
c.fillStyle = 'rgb(10,15,32)'; c.fillRect(x, y, S, S);
|
|
165
|
+
drawTile(c, x, y, S, o0, NAVY, CREAM, rot);
|
|
166
|
+
}
|
|
167
|
+
plabel(c, 'MODEL', MXb + 192, MYb - 40 + (1 - H.ease.back(XS(t, 2.7, 3.0))) * -60, { size: 30, align: 'center' });
|
|
168
|
+
const rows = [190, 330, 470];
|
|
169
|
+
rows.forEach((y, i) => arrow(c, 30, y + 45, MXb - 12, y + 45, H.ease.out(XS(t, 2.55 + i * 0.08, 2.95 + i * 0.08)), 4));
|
|
170
|
+
c.save(); c.beginPath(); c.rect(0, 0, MXb - 14, H_); c.clip();
|
|
171
|
+
const rr = H.rng(9), K = 34;
|
|
172
|
+
for (let k = 0; k < K; k++) {
|
|
173
|
+
const kind = rr() < 0.5 ? 'cat' : 'dog', ts = 2.6 + 3.0 * Math.pow(k / K, 0.8);
|
|
174
|
+
if (t < ts) continue; const x = -100 + (t - ts) * (560 + k * 18); if (x > MXb) continue;
|
|
175
|
+
exampleTile(c, kind, x, rows[k % 3], 90, kind);
|
|
176
|
+
}
|
|
177
|
+
c.restore();
|
|
178
|
+
if (t >= 2.7) {
|
|
179
|
+
const p = XS(t, 2.75, 5.0), n = p >= 1 ? 1e6 : Math.floor(Math.pow(10, 6 * H.ease.inOut(p))) - 1;
|
|
180
|
+
const w = plabel(c, Math.round(n).toLocaleString('en-US'), 30, 650, { size: 64 });
|
|
181
|
+
plabel(c, 'EXAMPLES', 30 + w + 16, 650, { size: 28, bg: ACC });
|
|
182
|
+
}
|
|
183
|
+
header(c);
|
|
184
|
+
finish(c, W, H_);
|
|
185
|
+
}
|
|
186
|
+
function sLoop(c, W, H_, t) {
|
|
187
|
+
paper(c, W, H_);
|
|
188
|
+
const M = maze();
|
|
189
|
+
const flipK = XS(t, 8.95, 9.25), flipped = flipK >= 1;
|
|
190
|
+
c.fillStyle = NAVY; c.fillRect(MX0, MY0, MC * MS, MR * MS);
|
|
191
|
+
for (let k = 0; k < MC * MR; k++) {
|
|
192
|
+
const [x, y] = tileXY(k), isF = M.flips.includes(k);
|
|
193
|
+
const o = flipped ? M.o2[k] : M.o[k];
|
|
194
|
+
if (isF && flipK > 0 && !flipped) continue;
|
|
195
|
+
drawTile(c, x, y, MS, o, null, CREAM, 0);
|
|
196
|
+
}
|
|
197
|
+
if (flipK > 0 && !flipped) for (const k of M.flips) { // the two weights quarter-turn out of their sockets
|
|
198
|
+
const [x, y] = tileXY(k), rot = H.ease.inOut(flipK) * Math.PI / 2;
|
|
199
|
+
c.fillStyle = 'rgb(10,15,32)'; c.fillRect(x, y, MS, MS);
|
|
200
|
+
drawTile(c, x, y, MS, M.o[k], NAVY, CREAM, rot);
|
|
201
|
+
}
|
|
202
|
+
c.strokeStyle = NAVY; c.lineWidth = 4; c.strokeRect(MX0, MY0, MC * MS, MR * MS);
|
|
203
|
+
// input
|
|
204
|
+
exampleTile(c, 'cat', 70, MY0 + R_IN * MS - 30, 140, null);
|
|
205
|
+
if (t >= 8.0) { // the label's answer, stacked in two printed blocks so it stays legible beside the maze
|
|
206
|
+
plabel(c, 'ANSWER:', 140, MY0 + R_IN * MS + 150, { size: 28, align: 'center', bg: ACC });
|
|
207
|
+
plabel(c, 'CAT', 140, MY0 + R_IN * MS + 208, { size: 28, align: 'center', bg: ACC });
|
|
208
|
+
} else plabel(c, 'NEW', 140, MY0 + R_IN * MS + 150, { size: 28, align: 'center', bg: NAVY });
|
|
209
|
+
arrow(c, 220, MY0 + R_IN * MS + 40, MX0 - 4, MY0 + R_IN * MS + 40, H.ease.out(XS(t, 6.4, 6.6)), 5);
|
|
210
|
+
// outputs
|
|
211
|
+
const outY = (rw) => MY0 + rw * MS + MS / 2;
|
|
212
|
+
arrow(c, MX0 + MC * MS + 4, outY(R_CAT), MX0 + MC * MS + 60, outY(R_CAT), H.ease.out(XS(t, 6.45, 6.65)), 4);
|
|
213
|
+
arrow(c, MX0 + MC * MS + 4, outY(R_DOG), MX0 + MC * MS + 60, outY(R_DOG), H.ease.out(XS(t, 6.5, 6.7)), 4);
|
|
214
|
+
const right = t >= 9.85;
|
|
215
|
+
plabel(c, 'CAT', MX0 + MC * MS + 70, outY(R_CAT), { size: 28, bg: right ? ACC : NAVY });
|
|
216
|
+
plabel(c, 'DOG', MX0 + MC * MS + 70, outY(R_DOG), { size: 28, bg: t >= 7.45 && t < 8.95 ? ACC : NAVY });
|
|
217
|
+
// forward pass #1: the band routes to DOG
|
|
218
|
+
if (t < 8.95) {
|
|
219
|
+
const pr = XS(t, 6.65, 7.45) * M.A.segs.length;
|
|
220
|
+
flowPath(c, M.A.segs, pr, ACC);
|
|
221
|
+
if (t >= 8.45) { // error signal runs back along the wrong path
|
|
222
|
+
const q = (1 - XS(t, 8.45, 8.9)) * M.A.segs.length; const [px, py] = pointOn(M.A.segs, q);
|
|
223
|
+
c.fillStyle = NAVY; c.beginPath(); c.arc(px, py, 15, 0, Math.PI * 2); c.fill(); c.strokeStyle = ACC; c.lineWidth = 4; c.stroke();
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const pr = XS(t, 9.35, 9.85) * M.B.segs.length;
|
|
227
|
+
flowPath(c, M.B.segs, pr, ACC);
|
|
228
|
+
}
|
|
229
|
+
const drop = (t0) => (1 - H.ease.back(XS(t, t0, t0 + 0.25))) * -50;
|
|
230
|
+
if (t >= 7.45 && t < 9.85) plabel(c, 'DOG?', MX0 + MC * MS + 70, outY(R_DOG) + 80 + drop(7.45), { size: 28 });
|
|
231
|
+
if (t >= 7.95 && t < 9.85) { // check: stamp a ring + X on the wrong output
|
|
232
|
+
const k = H.ease.out(XS(t, 7.95, 8.15));
|
|
233
|
+
const cx = MX0 + MC * MS + 112, cy = outY(R_DOG);
|
|
234
|
+
c.save(); c.strokeStyle = ACC; c.lineWidth = 7; c.beginPath(); c.arc(cx, cy, 50, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * k); c.stroke();
|
|
235
|
+
if (k >= 1) { c.lineWidth = 9; c.beginPath(); c.moveTo(cx - 30, cy - 30); c.lineTo(cx + 30, cy + 30); c.moveTo(cx + 30, cy - 30); c.lineTo(cx - 30, cy + 30); c.stroke(); }
|
|
236
|
+
c.restore();
|
|
237
|
+
const wy = cy - 80 + drop(8.0) * 0.5;
|
|
238
|
+
c.save(); c.font = '400 28px "Rubik Mono One"'; const ww = c.measureText('WRONG').width + 28 * 1.2; c.restore();
|
|
239
|
+
c.fillStyle = CREAM; c.fillRect(cx - 43, wy - 28 * 0.95 - 3, ww + 9, 28 * 1.9 + 9); // knockout so the block prints clean
|
|
240
|
+
plabel(c, 'WRONG', cx - 40, wy, { size: 28, bg: ACC });
|
|
241
|
+
}
|
|
242
|
+
if (t >= 8.85 && t < 9.9) { // adjust: the flipped tiles get ringed
|
|
243
|
+
M.flips.forEach((k) => { const [x, y] = tileXY(k); c.save(); c.strokeStyle = ACC; c.lineWidth = 6; c.setLineDash([10, 7]); c.lineDashOffset = -t * 40; c.strokeRect(x - 5, y - 5, MS + 10, MS + 10); c.restore(); });
|
|
244
|
+
const [fx, fy] = tileXY(M.flips[0]);
|
|
245
|
+
plabel(c, 'ADJUST: FLIP', fx + MS / 2 + 30, fy - 30 + drop(8.9), { size: 28, align: 'center', bg: ACC });
|
|
246
|
+
}
|
|
247
|
+
if (right) {
|
|
248
|
+
plabel(c, 'CAT ✓', MX0 + MC * MS + 70, outY(R_CAT) - 60 + drop(9.85), { size: 28, bg: ACC });
|
|
249
|
+
}
|
|
250
|
+
// loop strip
|
|
251
|
+
const words = ['GUESS', 'CHECK', 'ADJUST'];
|
|
252
|
+
const act = t < 7.45 ? -1 : t < 7.95 ? 0 : t < 8.45 ? 1 : t < 9.35 ? 2 : t < 9.95 ? 0 : Math.floor((t - 9.95) / 0.13) % 3;
|
|
253
|
+
let x = MX0;
|
|
254
|
+
words.forEach((w, i) => { x += plabel(c, w, x, 630, { size: 28, bg: i === act ? ACC : NAVY }) + 12; if (i < 2) { arrow(c, x, 630, x + 40, 630, 1, 5); x += 56; } });
|
|
255
|
+
plabel(c, '↻ ×1M', x + 8, 630, { size: 28, bg: CREAM, fg: NAVY });
|
|
256
|
+
header(c);
|
|
257
|
+
finish(c, W, H_);
|
|
258
|
+
}
|
|
259
|
+
function sProgress(c, W, H_, t) {
|
|
260
|
+
paper(c, W, H_);
|
|
261
|
+
plabel(c, 'ACCURACY', 60, 200, { size: 36 });
|
|
262
|
+
const p = H.ease.inOut(XS(t, 10.9, 12.3)), v = Math.round(61 + 36 * p);
|
|
263
|
+
const punch = t >= 12.3 ? 1 + 0.15 * Math.exp(-(t - 12.3) * 9) : 1;
|
|
264
|
+
c.save(); c.translate(60, 380); c.scale(punch, punch); c.font = '400 120px "Rubik Mono One"'; c.textBaseline = 'middle';
|
|
265
|
+
c.fillStyle = ACC; c.fillText(v + '%', 5, 5); c.fillStyle = NAVY; c.fillText(v + '%', 0, 0);
|
|
266
|
+
const w = c.measureText('97%').width; c.restore();
|
|
267
|
+
if (t >= 12.3) { const k = H.ease.out(XS(t, 12.3, 12.6)); c.save(); c.strokeStyle = ACC; c.lineWidth = 8; c.beginPath(); c.ellipse(60 + w / 2, 380, w / 2 + 40, 105, -0.05, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * k); c.stroke(); c.restore(); }
|
|
268
|
+
// bar chart: each bar a stack of tiles laid one by one
|
|
269
|
+
const x0 = 640, y0 = 600, S = 40, n = 12, r = H.rng(4);
|
|
270
|
+
for (let i = 0; i < n; i++) {
|
|
271
|
+
const acc = 61 + 36 * (1 - Math.exp(-i / 3.2)) / (1 - Math.exp(-(n - 1) / 3.2));
|
|
272
|
+
const tiles = Math.round((acc - 45) / 52 * 11);
|
|
273
|
+
let laid = 0; while (laid < tiles && t >= 10.85 + i * 0.11 + laid * 0.025) laid++;
|
|
274
|
+
if (laid) { c.fillStyle = i === n - 1 && t >= 12.3 ? ACC : NAVY; c.fillRect(x0 + i * (S + 7), y0 - laid * S, S, laid * S); }
|
|
275
|
+
for (let k = 0; k < laid; k++) drawTile(c, x0 + i * (S + 7), y0 - (k + 1) * S, S, r() < 0.5 ? 0 : 1, null, CREAM);
|
|
276
|
+
}
|
|
277
|
+
c.fillStyle = NAVY; c.fillRect(x0 - 6, y0 + 4, n * (S + 7) + 6, 4);
|
|
278
|
+
plabel(c, 'TRAINING ROUNDS →', x0, y0 + 46, { size: 28 });
|
|
279
|
+
header(c);
|
|
280
|
+
finish(c, W, H_);
|
|
281
|
+
}
|
|
282
|
+
function sTake(c, W, H_, t) {
|
|
283
|
+
c.drawImage(still(W, H_), 0, 0);
|
|
284
|
+
const k = H.ease.back(XS(t, 13.25, 13.55));
|
|
285
|
+
c.save(); c.translate(W / 2, 360); c.scale(k, k);
|
|
286
|
+
plabel(c, 'MORE EXAMPLES', 0, -46, { size: 44, align: 'center' });
|
|
287
|
+
plabel(c, '→ BETTER GUESSES', 0, 46, { size: 44, align: 'center', bg: ACC });
|
|
288
|
+
c.restore();
|
|
289
|
+
}
|
|
290
|
+
function lay(W, H_, fn, t) { const L = H.layer(W, H_); fn(L.getContext('2d'), W, H_, t); return L; }
|
|
291
|
+
// tiles re-laid: each 80px cell quarter-turns from the old scene to the new one in a diagonal sweep
|
|
292
|
+
function relay(c, W, H_, A, B, t0, t) {
|
|
293
|
+
const S = 80;
|
|
294
|
+
c.fillStyle = NAVY; c.fillRect(0, 0, W, H_);
|
|
295
|
+
for (let j = 0; j < 9; j++) for (let i = 0; i < 16; i++) {
|
|
296
|
+
const ts = t0 + 0.32 * ((i / 16) * 0.7 + (j / 9) * 0.3), k = H.clamp((t - ts) / 0.14);
|
|
297
|
+
const src = k < 0.5 ? A : B, x = i * S, y = j * S;
|
|
298
|
+
c.save(); c.translate(x + S / 2, y + S / 2); c.rotate(k * Math.PI / 2); const sq = Math.abs(Math.cos(k * Math.PI)) * 0.25 + 0.75; c.scale(k > 0 && k < 1 ? sq : 1, k > 0 && k < 1 ? sq : 1);
|
|
299
|
+
c.rotate(-(k < 0.5 ? 0 : 1) * Math.PI / 2 * 0); c.drawImage(src, x, y, S, S, -S / 2, -S / 2, S, S); c.restore();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function explainTR(ctx, W, H_, t) {
|
|
303
|
+
const scenes = [[0, sHook], [2.45, sData], [6.0, sLoop], [10.5, sProgress], [13.0, sTake]];
|
|
304
|
+
let idx = 0; for (let i = 0; i < scenes.length; i++) if (t >= scenes[i][0]) idx = i;
|
|
305
|
+
const t0 = scenes[idx][0];
|
|
306
|
+
if (idx > 0 && t < t0 + 0.5) relay(ctx, W, H_, lay(W, H_, scenes[idx - 1][1], t0 - 0.01), lay(W, H_, scenes[idx][1], t), t0, t);
|
|
307
|
+
else scenes[idx][1](ctx, W, H_, t);
|
|
308
|
+
H.grain(ctx, W, H_, 0.05, 5 + Math.floor(t * 12));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
registerStyle({
|
|
312
|
+
id: 'truchet',
|
|
313
|
+
name: 'Multi-scale Truchet',
|
|
314
|
+
category: 'Generative & Type',
|
|
315
|
+
description: 'Bold two-ink multi-scale Truchet tiling of winged quarter-circle tiles whose paths flow continuously across three scales; a third ink fills only the closed regions that fall inside the letters "AI", so the word surfaces from the maze.',
|
|
316
|
+
motion: 'Big tiles are laid in a diagonal sweep, then each scale subdivides in turn (paths re-routing as detail arrives); then the accent screen is pulled across and slides into register, revealing AI.',
|
|
317
|
+
fonts: ['400 540px "Rubik Mono One"', '400 20px "Rubik Mono One"'],
|
|
318
|
+
duration: 5,
|
|
319
|
+
frameT: 4.9,
|
|
320
|
+
readsPixels: true,
|
|
321
|
+
explainerDuration: 14.5,
|
|
322
|
+
explainerNote: 'The model is a Truchet maze whose tiles are its weights: an input band routes to the wrong output, the error runs back along the path and two tiles quarter-turn to re-route it; scenes change by re-laying the tiles in a diagonal sweep, labels are printed blocks with misregistered accent, and the accuracy chart is built from stacked tiles.',
|
|
323
|
+
explainer(ctx, W, H_, t) { explainTR(ctx, W, H_, t); },
|
|
324
|
+
// Sound: ceramic tiles. A tile turning into place is a short scrape and a hard clack; the scene re-lay is a domino
|
|
325
|
+
// run of clacks sweeping left to right with the diagonal; the ink band travelling through the maze is a marble
|
|
326
|
+
// rolling through a tile run (soft rumble, a click at each tile it crosses, taken from the real maze path); printed
|
|
327
|
+
// labels land with a dull block thump; the payoffs are wooden marimba notes. Timings mirror explainTR / sHook..sTake.
|
|
328
|
+
explainerSound(k) {
|
|
329
|
+
const D = 14.5, E = H.ease, M = maze();
|
|
330
|
+
const pan = (px) => H.clamp((px / 1280) * 2 - 1, -1, 1) * 0.7;
|
|
331
|
+
const when = (t0, t1, ease, u) => { for (let i = 0; i <= 400; i++) { const t = t0 + ((t1 - t0) * i) / 400; if (ease(i / 400) >= u - 1e-6) return t; } return t1; };
|
|
332
|
+
const back = (t0, d) => when(t0, t0 + d, E.back, 1);
|
|
333
|
+
// ceramic tile clack: bright hard tick + hollow body
|
|
334
|
+
const clack = (t, g, p, f = 640) => { k.noise(t, { dur: 0.018, filter: 'bandpass', f: 2100, q: 2.5, gain: 0.26 * g, a: 0.001, pan: p }); k.tone(t, { f: [[0, f], [0.04, f * 0.85]], dur: 0.06, gain: 0.1 * g, a: 0.001, pan: p }); };
|
|
335
|
+
const turn = (t, dur, g, p) => k.noise(t, { dur, filter: 'bandpass', f: [[0, 900], [dur, 1500]], q: 1.4, gain: 0.07 * g, a: dur * 0.6, pan: p }); // tile scraping round
|
|
336
|
+
// printed label block landing
|
|
337
|
+
const block = (t, g, p) => { k.tone(t, { f: [[0, 170], [0.06, 110]], dur: 0.09, gain: 0.22 * g, a: 0.002, pan: p }); k.noise(t, { dur: 0.03, filter: 'bandpass', f: 1100, q: 1, gain: 0.1 * g, a: 0.001, pan: p }); };
|
|
338
|
+
const marimba = (t, n, g, p) => { const f = k.hz(n); k.tone(t, { f, dur: 0.7, gain: 0.16 * g, a: 0.002, verb: 0.2, pan: p }); if (f * 4 < 2500) k.tone(t, { f: f * 4, dur: 0.07, gain: 0.04 * g, a: 0.001, pan: p }); k.noise(t, { dur: 0.012, filter: 'bandpass', f: 1500, q: 1, gain: 0.05 * g, pan: p }); };
|
|
339
|
+
const zip = (t, dur, p) => k.noise(t, { dur, color: 'pink', filter: 'bandpass', f: [[0, 600], [dur, 1500]], q: 1.4, gain: 0.06, a: dur * 0.5, pan: p, minor: true });
|
|
340
|
+
// re-lay: cell (i,j) starts at t0 + 0.32 (0.7 i/16 + 0.3 j/9) and lands 0.14 s later -> a domino run left to right
|
|
341
|
+
const relay = (t0) => {
|
|
342
|
+
k.noise(t0 + 0.05, { dur: 0.45, color: 'pink', filter: 'bandpass', f: [[0, 700], [0.45, 1300]], q: 0.8, gain: 0.09, a: 0.15 });
|
|
343
|
+
k.withMinor(() => { for (let i = 0; i < 16; i += 1) { const tl = t0 + 0.14 + 0.32 * (0.7 * i / 16 + 0.15); clack(tl, 0.55 + 0.25 * ((i * 7) % 3) / 2, pan(i * 80 + 40), 560 + ((i * 5) % 4) * 40); } });
|
|
344
|
+
clack(t0 + 0.47, 0.9, 0.6, 520); // last corner settles
|
|
345
|
+
};
|
|
346
|
+
// the band rolling through the maze: rumble + a click per tile crossed, panned with the tile
|
|
347
|
+
const roll = (segs, t0, t1, rev, g = 1) => {
|
|
348
|
+
const d = t1 - t0, n = segs.length;
|
|
349
|
+
k.noise(t0, { dur: d, color: 'brown', filter: 'lowpass', f: 420, gain: 0.14 * g, a: 0.06, hold: d - 0.12, pan: pan(MX0 + (rev ? MC * MS : 0)) });
|
|
350
|
+
k.withMinor(() => { for (let q = 1; q < n; q++) { const s = segs[rev ? n - 1 - q : q], t = t0 + (q / n) * d; k.noise(t, { dur: 0.014, filter: 'bandpass', f: 1500 + (q % 3) * 150, q: 2, gain: 0.12 * g, a: 0.001, pan: pan(MX0 + s.i * MS + MS / 2) }); k.tone(t, { f: rev ? 420 - q * 8 : 300 + q * 8, dur: 0.035, gain: 0.05 * g, pan: pan(MX0 + s.i * MS + MS / 2) }); } });
|
|
351
|
+
};
|
|
352
|
+
const outX = MX0 + MC * MS + 100;
|
|
353
|
+
|
|
354
|
+
k.bed('room', 0, D, { gain: 0.3 });
|
|
355
|
+
|
|
356
|
+
// ---- hook: title block pops (back 0.15-0.55), slides into the corner (1.7-2.3)
|
|
357
|
+
block(back(0.15, 0.4), 1.2, 0); clack(back(0.15, 0.4), 0.5, 0, 500);
|
|
358
|
+
k.noise(1.7, { dur: 0.6, color: 'pink', filter: 'lowpass', f: [[0, 600], [0.6, 260]], gain: 0.028, a: 0.25, minor: true });
|
|
359
|
+
|
|
360
|
+
// ---- data: re-lay, arrows, MODEL, tiles of the model flip as examples pour in (same rng(55) draws), counter
|
|
361
|
+
relay(2.45);
|
|
362
|
+
[0, 1, 2].forEach((i) => zip(2.55 + i * 0.08, 0.4, -0.4 + i * 0.05));
|
|
363
|
+
block(back(2.7, 0.3), 0.8, pan(1020));
|
|
364
|
+
{ const r = H.rng(55), flips = [];
|
|
365
|
+
for (let c = 0; c < 36; c++) { r(); const tf = 2.8 + r() * 3.2; if (tf + 0.15 < 6.0) flips.push([tf, c]); }
|
|
366
|
+
flips.sort((a, b) => a[0] - b[0]); let last = -1;
|
|
367
|
+
flips.forEach(([tf, c]) => { if (tf - last < 0.14) return; last = tf; k.withMinor(() => clack(tf + 0.15, 0.6, pan(830 + (c % 6) * 64 + 32), 600 + (c % 4) * 30)); }); }
|
|
368
|
+
{ let prev = 0, last = -1; for (let t = 2.76; t <= 4.98; t += 0.002) { const s = Math.floor(E.inOut(XS(t, 2.75, 5.0)) * 30); if (s !== prev && t - last >= 1 / 8) { k.play('tock', t, { f: 380, gain: 0.3, pan: -0.6, minor: true }); last = t; } prev = s; } }
|
|
369
|
+
block(5.0, 1.4, -0.5); clack(5.0, 1, -0.5, 480); // 1,000,000 set down
|
|
370
|
+
|
|
371
|
+
// ---- loop: re-lay, arrows, band rolls to DOG, wrong, error rolls back, two tiles turn, band rolls to CAT
|
|
372
|
+
relay(6.0);
|
|
373
|
+
zip(6.4, 0.2, -0.4); zip(6.45, 0.2, 0.55); zip(6.5, 0.2, 0.55);
|
|
374
|
+
roll(M.A.segs, 6.65, 7.45, false);
|
|
375
|
+
marimba(7.45, 'G3', 0.8, pan(outX)); block(back(7.45, 0.25), 0.7, pan(outX)); // lands on DOG, DOG? tag
|
|
376
|
+
k.noise(7.95, { dur: 0.2, filter: 'bandpass', f: [[0, 700], [0.2, 1200]], q: 1.2, gain: 0.07, a: 0.12, pan: pan(outX), minor: true }); // ring drawn
|
|
377
|
+
block(back(8.0, 0.25), 0.8, pan(outX));
|
|
378
|
+
k.play('thud', 8.15, { f: 120, gain: 0.6, pan: pan(outX) }); marimba(8.15, 'F#3', 0.8, pan(outX)); marimba(8.3, 'C3', 0.8, pan(outX)); // X: wrong
|
|
379
|
+
roll(M.A.segs, 8.45, 8.9, true, 0.9); // error runs back along the path
|
|
380
|
+
block(back(8.9, 0.25), 0.7, pan(MX0 + (M.flips[0] % MC) * MS));
|
|
381
|
+
M.flips.forEach((f, i) => { const p = pan(MX0 + (f % MC) * MS + MS / 2); turn(8.95 + i * 0.02, 0.3, 1, p); clack(9.25 + i * 0.018, 1.1, p, 560 - i * 50); }); // adjust: flip
|
|
382
|
+
roll(M.B.segs, 9.35, 9.85, false);
|
|
383
|
+
block(back(9.85, 0.25), 0.8, pan(outX));
|
|
384
|
+
marimba(9.85, 'C4', 1, pan(outX)); marimba(9.93, 'G4', 0.9, pan(outX)); marimba(10.01, 'C5', 0.8, pan(outX)); // CAT ✓
|
|
385
|
+
|
|
386
|
+
// ---- progress: re-lay, tile columns stack (tk = 10.85 + i*0.11), counter, 97%
|
|
387
|
+
relay(10.5);
|
|
388
|
+
k.withMinor(() => { for (let i = 0; i < 12; i++) { const acc = 61 + 36 * (1 - Math.exp(-i / 3.2)) / (1 - Math.exp(-11 / 3.2)), n = Math.round((acc - 45) / 52 * 11); clack(10.85 + i * 0.11, 0.55, pan(640 + i * 47), 420 + n * 18); if (n > 4) clack(10.85 + i * 0.11 + (n - 1) * 0.025, 0.3, pan(640 + i * 47), 520 + n * 18); } });
|
|
389
|
+
{ let prev = 0, last = -1; for (let t = 10.91; t <= 12.28; t += 0.002) { const s = Math.floor(E.inOut(XS(t, 10.9, 12.3)) * 36), near = ((t - 10.85) % 0.11) < 0.05; if (s !== prev && !near && t - last >= 1 / 7) { k.play('tock', t, { f: 380, gain: 0.28, pan: -0.6, minor: true }); last = t; } prev = s; } }
|
|
390
|
+
k.play('impact', 12.3, { gain: 1.1, pan: -0.4 });
|
|
391
|
+
clack(12.3, 1.3, -0.4, 450);
|
|
392
|
+
['C4', 'E4', 'G4', 'C5'].forEach((n, i) => marimba(12.33 + i * 0.03, n, 1.1, (i - 1.5) * 0.25)); // the 97% hit
|
|
393
|
+
k.noise(12.3, { dur: 0.3, filter: 'bandpass', f: [[0, 700], [0.3, 1200]], q: 1.2, gain: 0.06, a: 0.15, pan: -0.4, minor: true }); // ring drawn
|
|
394
|
+
|
|
395
|
+
// ---- takeaway: re-lay into the tiling, labels pop, resolving marimba
|
|
396
|
+
relay(13.0);
|
|
397
|
+
const tk = back(13.25, 0.3);
|
|
398
|
+
block(tk, 1.2, 0);
|
|
399
|
+
['G3', 'C4', 'E4', 'G4', 'C5'].forEach((n, i) => marimba(tk + 0.06 + i * 0.07, n, 0.65 - i * 0.04, (i - 2) * 0.2));
|
|
400
|
+
},
|
|
401
|
+
draw(ctx, W, H_, t) {
|
|
402
|
+
const r = H.rng(808);
|
|
403
|
+
// ---------- letter mask ----------
|
|
404
|
+
const ml = H.layer(W, H_), mc = ml.getContext('2d');
|
|
405
|
+
mc.fillStyle = '#000'; mc.fillRect(0, 0, W, H_);
|
|
406
|
+
mc.fillStyle = '#fff'; mc.textAlign = 'center'; mc.textBaseline = 'alphabetic';
|
|
407
|
+
mc.font = '400 540px "Rubik Mono One"';
|
|
408
|
+
try { mc.letterSpacing = "10px"; } catch (e) {}
|
|
409
|
+
mc.textBaseline = 'middle';
|
|
410
|
+
mc.fillText("AI", W / 2 + 6, 372);
|
|
411
|
+
const md = mc.getImageData(0, 0, W, H_).data;
|
|
412
|
+
const inMask = (x, y) => { x = x | 0; y = y | 0; if (x < 0 || y < 0 || x >= W || y >= H_) return 0; return md[(y * W + x) * 4] > 127 ? 1 : 0; };
|
|
413
|
+
|
|
414
|
+
// ---------- quadtree of tiles ----------
|
|
415
|
+
const S0 = 160, tiles = [];
|
|
416
|
+
const flipPhase = 1.8; // fixed: keeps the finished tiling identical to the approved still
|
|
417
|
+
function classify(x, y, s) {
|
|
418
|
+
let n = 0, k = 0;
|
|
419
|
+
for (let j = 0; j <= 6; j++) for (let i = 0; i <= 6; i++) { n++; k += inMask(x + (i / 6) * s, y + (j / 6) * s); }
|
|
420
|
+
return k === 0 ? 0 : k === n ? 2 : 1; // 0 out, 1 edge, 2 inside
|
|
421
|
+
}
|
|
422
|
+
function build(x, y, s, L) {
|
|
423
|
+
const c = classify(x, y, s);
|
|
424
|
+
const pRand = [0.55, 0.32, 0.12][L] || 0;
|
|
425
|
+
let split = false;
|
|
426
|
+
if (L < 3 && c === 1) split = true; // letter edges: refine to the smallest scale
|
|
427
|
+
else if (L < 3 && c === 2) split = true; // letter interiors: finest scale too
|
|
428
|
+
else if (L < 2 && r() < pRand) split = true;
|
|
429
|
+
else if (L === 2 && c === 0 && r() < 0.08) split = true;
|
|
430
|
+
if (split) {
|
|
431
|
+
const h = s / 2;
|
|
432
|
+
build(x, y, h, L + 1); build(x + h, y, h, L + 1); build(x, y + h, h, L + 1); build(x + h, y + h, h, L + 1);
|
|
433
|
+
} else {
|
|
434
|
+
// orientation: seeded, with a slow wave of flips for animation
|
|
435
|
+
const base = r() < 0.5 ? 0 : 1;
|
|
436
|
+
const flip = Math.sin(x * 0.004 + y * 0.003 - flipPhase) > 0.985 ? 1 : 0;
|
|
437
|
+
tiles.push({ x, y, s, L, o: base ^ flip });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const oy = -(S0 * 5 - H_) / 2;
|
|
441
|
+
for (let j = 0; j < 5; j++) for (let i = 0; i < 8; i++) build(i * S0, oy + j * S0, S0, 0);
|
|
442
|
+
// balance the quadtree: neighbouring tiles may differ by at most one level, otherwise the
|
|
443
|
+
// winged edge patterns can't meet (Carlson's construction relies on 1-level steps)
|
|
444
|
+
const F = 20, GX = W / F, GY = Math.ceil((S0 * 5) / F);
|
|
445
|
+
for (let pass = 0; pass < 6; pass++) {
|
|
446
|
+
const grid = new Int8Array(GX * GY);
|
|
447
|
+
for (const T of tiles) {
|
|
448
|
+
const gx0 = Math.round(T.x / F), gy0 = Math.round((T.y - oy) / F), n = Math.round(T.s / F);
|
|
449
|
+
for (let j = 0; j < n; j++) for (let i = 0; i < n; i++) grid[(gy0 + j) * GX + gx0 + i] = T.L;
|
|
450
|
+
}
|
|
451
|
+
const at = (i, j) => (i < 0 || j < 0 || i >= GX || j >= GY ? -1 : grid[j * GX + i]);
|
|
452
|
+
let changed = false;
|
|
453
|
+
const next = [];
|
|
454
|
+
for (const T of tiles) {
|
|
455
|
+
const gx0 = Math.round(T.x / F), gy0 = Math.round((T.y - oy) / F), n = Math.round(T.s / F);
|
|
456
|
+
let mx = -1;
|
|
457
|
+
for (let k = 0; k < n; k++) { mx = Math.max(mx, at(gx0 - 1, gy0 + k), at(gx0 + n, gy0 + k), at(gx0 + k, gy0 - 1), at(gx0 + k, gy0 + n)); }
|
|
458
|
+
if (mx > T.L + 1) {
|
|
459
|
+
changed = true;
|
|
460
|
+
const h = T.s / 2;
|
|
461
|
+
for (const [dx, dy] of [[0, 0], [h, 0], [0, h], [h, h]]) next.push({ x: T.x + dx, y: T.y + dy, s: h, L: T.L + 1, o: r() < 0.5 ? 0 : 1 });
|
|
462
|
+
} else next.push(T);
|
|
463
|
+
}
|
|
464
|
+
tiles.length = 0; tiles.push(...next);
|
|
465
|
+
if (!changed) break;
|
|
466
|
+
}
|
|
467
|
+
tiles.sort((a, b) => a.L - b.L);
|
|
468
|
+
|
|
469
|
+
// ---------- build: which scale is visible where (tiles laid on twos) ----------
|
|
470
|
+
const tq = Math.floor(t * 15) / 15;
|
|
471
|
+
const st0 = [0.1, 0.95, 1.55, 2.1], stD = [0.8, 0.55, 0.5, 0.5];
|
|
472
|
+
const startAt = (l, ax, ay, s) => st0[l] + stD[l] * (((ax + s / 2) / W) * 0.65 + ((ay + s / 2 - oy) / 800) * 0.35);
|
|
473
|
+
const hashO = (x, y, s) => (H.rng(((x + 1000) * 7919 + (y + 1000) * 104729 + s * 31) >>> 0)() < 0.5 ? 0 : 1);
|
|
474
|
+
const shown = new Map();
|
|
475
|
+
for (const T of tiles) {
|
|
476
|
+
let pick = null;
|
|
477
|
+
for (let l = T.L; l >= 0; l--) {
|
|
478
|
+
const sl = S0 / (1 << l), ax = Math.floor(T.x / sl) * sl, ay = oy + Math.floor((T.y - oy) / sl) * sl;
|
|
479
|
+
const st = startAt(l, ax, ay, sl);
|
|
480
|
+
if (tq >= st) { pick = l === T.L ? { ...T, age: tq - st } : { x: ax, y: ay, s: sl, L: l, o: hashO(ax, ay, sl), age: tq - st }; break; }
|
|
481
|
+
}
|
|
482
|
+
if (pick) shown.set(pick.L + ':' + pick.x + ':' + pick.y, pick);
|
|
483
|
+
}
|
|
484
|
+
// hold: a few large tiles outside the word make a quarter-turn (orientation flip)
|
|
485
|
+
const flips = [[0, 2], [7, 1], [6, 3], [1, 0]];
|
|
486
|
+
const disp = [...shown.values()].sort((a, b) => a.L - b.L);
|
|
487
|
+
for (const T of disp) {
|
|
488
|
+
if (T.L !== 0) continue;
|
|
489
|
+
const fi = flips.findIndex(([i, j]) => T.x === i * S0 && T.y === oy + j * S0);
|
|
490
|
+
if (fi < 0) continue;
|
|
491
|
+
const k = H.clamp((t - (3.55 + fi * 0.09)) / 0.2);
|
|
492
|
+
T.rot = H.ease.inOut(k) * Math.PI / 2;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ---------- draw the two-ink tiling (dark/light only) ----------
|
|
496
|
+
const lay = H.layer(W, H_), lc = lay.getContext('2d');
|
|
497
|
+
lc.fillStyle = `rgb(${C_LIGHT[0]},${C_LIGHT[1]},${C_LIGHT[2]})`; lc.fillRect(0, 0, W, H_); // bare paper
|
|
498
|
+
const ink = (c) => `rgb(${c[0]},${c[1]},${c[2]})`;
|
|
499
|
+
for (const T of disp) {
|
|
500
|
+
const fill = T.L % 2 ? C_LIGHT : C_DARK, path = T.L % 2 ? C_DARK : C_LIGHT;
|
|
501
|
+
const { x, y, s } = T;
|
|
502
|
+
lc.save();
|
|
503
|
+
const pop = T.age < 0.07 ? 0.86 : 1; // freshly laid tile settles in (2 frames)
|
|
504
|
+
if (pop !== 1 || T.rot) { lc.translate(x + s / 2, y + s / 2); if (T.rot) lc.rotate(T.rot); lc.scale(pop, pop); lc.translate(-x - s / 2, -y - s / 2); }
|
|
505
|
+
lc.fillStyle = ink(fill); lc.fillRect(x, y, s, s);
|
|
506
|
+
// wings
|
|
507
|
+
lc.beginPath();
|
|
508
|
+
[[x, y], [x + s, y], [x, y + s], [x + s, y + s]].forEach(([px, py]) => { lc.moveTo(px + s / 3, py); lc.arc(px, py, s / 3, 0, TAU); });
|
|
509
|
+
lc.fill();
|
|
510
|
+
// bands
|
|
511
|
+
lc.strokeStyle = ink(path); lc.lineWidth = s / 3; lc.lineCap = 'butt';
|
|
512
|
+
lc.beginPath();
|
|
513
|
+
if (T.o === 0) { lc.arc(x, y, s / 2, 0, Math.PI / 2); lc.moveTo(x + s - s / 2, y + s); lc.arc(x + s, y + s, s / 2, Math.PI, Math.PI * 1.5); }
|
|
514
|
+
else { lc.moveTo(x + s, y + s / 2); lc.arc(x + s, y, s / 2, Math.PI / 2, Math.PI); lc.moveTo(x + s / 2, y + s); lc.arc(x, y + s, s / 2, -Math.PI / 2, 0); }
|
|
515
|
+
lc.stroke();
|
|
516
|
+
lc.restore();
|
|
517
|
+
}
|
|
518
|
+
const src = lc.getImageData(0, 0, W, H_).data;
|
|
519
|
+
|
|
520
|
+
// ---------- flood-label light regions; flag those inside the letters ----------
|
|
521
|
+
const N = W * H_;
|
|
522
|
+
const gv = new Float32Array(N), lab = new Int32Array(N).fill(-1);
|
|
523
|
+
const dl = C_LIGHT[0] - C_DARK[0];
|
|
524
|
+
for (let i = 0; i < N; i++) gv[i] = H.clamp((src[i * 4] - C_DARK[0]) / dl);
|
|
525
|
+
const stack = new Int32Array(N);
|
|
526
|
+
const cnt = [], inC = [];
|
|
527
|
+
let nl = 0;
|
|
528
|
+
for (let i = 0; i < N; i++) {
|
|
529
|
+
if (lab[i] !== -1 || gv[i] < 0.5) continue;
|
|
530
|
+
let sp = 0; stack[sp++] = i; lab[i] = nl; let c = 0, k = 0;
|
|
531
|
+
while (sp) {
|
|
532
|
+
const p = stack[--sp]; c++; k += md[p * 4] > 127 ? 1 : 0;
|
|
533
|
+
const x = p % W;
|
|
534
|
+
if (x > 0 && lab[p - 1] === -1 && gv[p - 1] >= 0.5) { lab[p - 1] = nl; stack[sp++] = p - 1; }
|
|
535
|
+
if (x < W - 1 && lab[p + 1] === -1 && gv[p + 1] >= 0.5) { lab[p + 1] = nl; stack[sp++] = p + 1; }
|
|
536
|
+
if (p >= W && lab[p - W] === -1 && gv[p - W] >= 0.5) { lab[p - W] = nl; stack[sp++] = p - W; }
|
|
537
|
+
if (p < N - W && lab[p + W] === -1 && gv[p + W] >= 0.5) { lab[p + W] = nl; stack[sp++] = p + W; }
|
|
538
|
+
}
|
|
539
|
+
cnt.push(c); inC.push(k); nl++;
|
|
540
|
+
}
|
|
541
|
+
const flag = new Uint8Array(nl);
|
|
542
|
+
// letters fill in over time (animation); at frameT all flagged
|
|
543
|
+
const reveal = 1;
|
|
544
|
+
for (let i = 0; i < nl; i++) flag[i] = inC[i] / cnt[i] > 0.5 && cnt[i] < 60000 && ((i * 2654435761) % 1000) / 1000 < reveal * 1.01 ? 1 : 0;
|
|
545
|
+
// per-tile letter flag (finest tile wins): regions mostly inside the word take the
|
|
546
|
+
// accent whole; long paths that cross the letter edge are cut at the tile boundary
|
|
547
|
+
const fl = H.layer(W, H_), fc = fl.getContext('2d');
|
|
548
|
+
fc.fillStyle = '#000'; fc.fillRect(0, 0, W, H_);
|
|
549
|
+
for (const T of tiles) { fc.fillStyle = inMask(T.x + T.s / 2, T.y + T.s / 2) ? '#fff' : '#000'; fc.fillRect(T.x, T.y, T.s, T.s); }
|
|
550
|
+
const fd = fc.getImageData(0, 0, W, H_).data;
|
|
551
|
+
const accentAt = (p) => {
|
|
552
|
+
const L0 = lab[p];
|
|
553
|
+
if (L0 >= 0 && flag[L0]) return 1;
|
|
554
|
+
if (L0 >= 0 && inC[L0] / cnt[L0] > 0.9) return 0;
|
|
555
|
+
return fd[p * 4] > 127 ? 1 : 0;
|
|
556
|
+
};
|
|
557
|
+
const accentAtOld = (p) => {
|
|
558
|
+
let L = lab[p];
|
|
559
|
+
if (L >= 0) return flag[L];
|
|
560
|
+
const x = p % W;
|
|
561
|
+
if (x > 0 && lab[p - 1] >= 0) return flag[lab[p - 1]];
|
|
562
|
+
if (x < W - 1 && lab[p + 1] >= 0) return flag[lab[p + 1]];
|
|
563
|
+
if (p >= W && lab[p - W] >= 0) return flag[lab[p - W]];
|
|
564
|
+
if (p < N - W && lab[p + W] >= 0) return flag[lab[p + W]];
|
|
565
|
+
return 0;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// ---------- composite as a 3-colour screen print ----------
|
|
569
|
+
const out = ctx.createImageData(W, H_), d = out.data;
|
|
570
|
+
const nz = H.makeNoise(17), rr = H.rng(3);
|
|
571
|
+
// accent screen: pulled left->right by the squeegee, then slides into (mis)register
|
|
572
|
+
const pull = H.ease.inOut(H.clamp((t - 2.75) / 0.6));
|
|
573
|
+
const xw = -40 + 1360 * pull;
|
|
574
|
+
const reg = H.ease.out(H.clamp((t - 3.0) / 0.9));
|
|
575
|
+
const MX = Math.round(2 + 16 * (1 - reg)), MY = Math.round(1 + 9 * (1 - reg));
|
|
576
|
+
for (let y = 0; y < H_; y++) {
|
|
577
|
+
for (let x = 0; x < W; x++) {
|
|
578
|
+
const p = y * W + x;
|
|
579
|
+
const g = gv[p];
|
|
580
|
+
// accent screen is offset: look up the flag at the shifted position
|
|
581
|
+
const sx = Math.min(W - 1, Math.max(0, x - MX)), sy = Math.min(H_ - 1, Math.max(0, y - MY));
|
|
582
|
+
const accHere = accentAt(sy * W + sx);
|
|
583
|
+
const gs = gv[sy * W + sx];
|
|
584
|
+
let Lc = C_LIGHT;
|
|
585
|
+
// accent ink lays where the shifted light region is flagged
|
|
586
|
+
let ar = 0;
|
|
587
|
+
const pulled = accHere && x < xw + (y - 360) * 0.25;
|
|
588
|
+
if (pulled) ar = H.clamp(gs * 1.2);
|
|
589
|
+
const ink1 = 1 + nz.n2(x * 0.02, y * 0.02) * 0.03 + (rr() - 0.5) * 0.05; // ink film unevenness
|
|
590
|
+
let R = C_DARK[0] + (C_LIGHT[0] - C_DARK[0]) * g, G = C_DARK[1] + (C_LIGHT[1] - C_DARK[1]) * g, B = C_DARK[2] + (C_LIGHT[2] - C_DARK[2]) * g;
|
|
591
|
+
if (ar > 0) {
|
|
592
|
+
// accent printed under the dark ink: multiply-ish over light, hidden under dark
|
|
593
|
+
const a = ar * (0.15 + 0.85 * g);
|
|
594
|
+
R = R + (C_ACC[0] * (R / 255) * 1.04 - R) * a; G = G + (C_ACC[1] * (G / 255) * 1.04 - G) * a; B = B + (C_ACC[2] * (B / 255) * 1.04 - B) * a;
|
|
595
|
+
}
|
|
596
|
+
if (pulled) { // navy inside the letters warms toward the accent (accent printed under a translucent navy)
|
|
597
|
+
const lift = (1 - g) * 0.3;
|
|
598
|
+
R += (C_ACC[0] * 0.55 - R) * lift; G += (C_ACC[1] * 0.55 - G) * lift; B += (C_ACC[2] * 0.55 - B) * lift;
|
|
599
|
+
}
|
|
600
|
+
const o = p * 4;
|
|
601
|
+
d[o] = R * ink1; d[o + 1] = G * ink1; d[o + 2] = B * ink1; d[o + 3] = 255;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
ctx.putImageData(out, 0, 0);
|
|
605
|
+
H.grain(ctx, W, H_, 0.05, 5 + Math.floor(t * 12));
|
|
606
|
+
H.vignette(ctx, W, H_, 0.12);
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 86,
|
|
3
|
+
"name": "Multi-scale Truchet",
|
|
4
|
+
"nameZh": "多尺度特鲁歇拼砖",
|
|
5
|
+
"family": "Generative & Type",
|
|
6
|
+
"familyZh": "生成艺术与字体",
|
|
7
|
+
"description": "Bold two-ink multi-scale Truchet tiling of winged quarter-circle tiles whose paths flow continuously across three scales; a third ink fills only the closed regions that fall inside the letters \"AI\", so the word surfaces from the maze.",
|
|
8
|
+
"motionNotes": "Motion: Big tiles are laid in a diagonal sweep, then each scale subdivides in turn (paths re-routing as detail arrives); then the accent screen is pulled across and slides into register, revealing AI.",
|
|
9
|
+
"explainerNotes": "Explainer: The model is a Truchet maze whose tiles are its weights: an input band routes to the wrong output, the error runs back along the path and two tiles quarter-turn to re-route it; scenes change by re-laying the tiles in a diagonal sweep, labels are printed blocks with misregistered accent, and the accuracy chart is built from stacked tiles.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "truchet",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|