@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
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 78,
|
|
3
|
+
"name": "Sketchbook Graphite",
|
|
4
|
+
"nameZh": "石墨素描本",
|
|
5
|
+
"family": "Hand-Drawn",
|
|
6
|
+
"familyZh": "手绘",
|
|
7
|
+
"description": "A graphite study in a spiral sketchbook: faint construction circles and axes under confident darker contours, directional hatching that breaks on the paper tooth, smudged tones, kneaded-eraser highlights and a margin note.",
|
|
8
|
+
"motionNotes": "Motion: Construction lines ghost in first, contours firm up over them, then hatching sweeps across the shadow side while the eraser lifts the highlight.",
|
|
9
|
+
"explainerNotes": "Explainer: A pencil works across a wide sketchbook spread (faint construction pass first, then a firm contour, crescent hatching for form) while the camera follows, then the page physically turns to a fresh sheet for the progress chart; numbers are pencilled and re-pencilled as they count.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "sketchbook-pencil",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
{
|
|
2
|
+
const COLS = 40, ROWS = 25;
|
|
3
|
+
const PAL = { k: '#000000', r: '#ff0000', g: '#00ff00', y: '#ffff00', b: '#0000ff', m: '#ff00ff', c: '#00ffff', w: '#ffffff' };
|
|
4
|
+
const PKEYS = Object.keys(PAL);
|
|
5
|
+
const PRGB = PKEYS.map((k) => H.hex(PAL[k]));
|
|
6
|
+
|
|
7
|
+
function newGrid() {
|
|
8
|
+
const g = [];
|
|
9
|
+
for (let r = 0; r < ROWS; r++) { const row = []; for (let c = 0; c < COLS; c++) row.push({ ch: ' ', fg: 'w', bg: 'k', mos: 0, dh: 0 }); g.push(row); }
|
|
10
|
+
return g;
|
|
11
|
+
}
|
|
12
|
+
// put text: string, starting col, fg/bg; dh = 1 means double height (occupies row and row+1)
|
|
13
|
+
function put(g, row, col, str, fg = 'w', bg = null, dh = 0) {
|
|
14
|
+
for (let i = 0; i < str.length && col + i < COLS; i++) {
|
|
15
|
+
const cell = g[row][col + i];
|
|
16
|
+
cell.ch = str[i]; cell.fg = fg; cell.mos = 0; cell.dh = dh; if (bg) cell.bg = bg;
|
|
17
|
+
if (dh && row + 1 < ROWS) { const b = g[row + 1][col + i]; b.ch = ' '; b.dh = 2; b.mos = 0; if (bg) b.bg = bg; }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function band(g, row, c0, c1, bg) { for (let c = c0; c <= c1; c++) g[row][c].bg = bg; }
|
|
21
|
+
|
|
22
|
+
// Rasterize a canvas drawing into sextant mosaic cells with one foreground colour per cell (like the real thing).
|
|
23
|
+
function mosaic(g, col0, row0, cols, rows, bg, drawFn) {
|
|
24
|
+
const S = 4, sw = cols * 2, sh = rows * 3;
|
|
25
|
+
const L = H.layer(sw * S, sh * S), c = L.getContext('2d', { willReadFrequently: true });
|
|
26
|
+
c.scale(S, S); drawFn(c, sw, sh);
|
|
27
|
+
const d = c.getImageData(0, 0, sw * S, sh * S).data;
|
|
28
|
+
// per-subpixel: dominant palette index (or -1)
|
|
29
|
+
const sub = new Int8Array(sw * sh).fill(-1);
|
|
30
|
+
for (let y = 0; y < sh; y++) for (let x = 0; x < sw; x++) {
|
|
31
|
+
const counts = new Array(8).fill(0); let cov = 0;
|
|
32
|
+
for (let j = 0; j < S; j++) for (let i = 0; i < S; i++) {
|
|
33
|
+
const p = ((y * S + j) * sw * S + (x * S + i)) * 4;
|
|
34
|
+
if (d[p + 3] < 128) continue;
|
|
35
|
+
cov++;
|
|
36
|
+
let best = 0, bd = 1e9;
|
|
37
|
+
for (let k = 0; k < 8; k++) { const q = PRGB[k]; const dd = (q[0] - d[p]) ** 2 + (q[1] - d[p + 1]) ** 2 + (q[2] - d[p + 2]) ** 2; if (dd < bd) { bd = dd; best = k; } }
|
|
38
|
+
counts[best]++;
|
|
39
|
+
}
|
|
40
|
+
if (cov >= S * S * 0.5) { let m = 0; for (let k = 1; k < 8; k++) if (counts[k] > counts[m]) m = k; sub[y * sw + x] = m; }
|
|
41
|
+
}
|
|
42
|
+
for (let r = 0; r < rows; r++) for (let cc = 0; cc < cols; cc++) {
|
|
43
|
+
const counts = new Array(8).fill(0);
|
|
44
|
+
for (let j = 0; j < 3; j++) for (let i = 0; i < 2; i++) { const v = sub[(r * 3 + j) * sw + cc * 2 + i]; if (v >= 0 && PKEYS[v] !== bg) counts[v]++; }
|
|
45
|
+
let m = -1, mc = 0; for (let k = 0; k < 8; k++) if (counts[k] > mc) { mc = counts[k]; m = k; }
|
|
46
|
+
const cell = g[row0 + r][col0 + cc];
|
|
47
|
+
cell.bg = bg; cell.ch = ' '; cell.dh = 0;
|
|
48
|
+
if (m < 0) { cell.mos = 0; continue; }
|
|
49
|
+
let bits = 0;
|
|
50
|
+
for (let j = 0; j < 3; j++) for (let i = 0; i < 2; i++) if (sub[(r * 3 + j) * sw + cc * 2 + i] === m) bits |= 1 << (j * 2 + i);
|
|
51
|
+
cell.mos = bits; cell.fg = PKEYS[m];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildPage(t) {
|
|
56
|
+
const g = newGrid();
|
|
57
|
+
// Row 0 header
|
|
58
|
+
const sec = (45 + Math.floor(t)) % 60, mn = 23 + Math.floor((45 + t) / 60);
|
|
59
|
+
const searching = t < 0.9 ? (Math.floor(t * 24) * 37 % 899 + 100).toString(16).toUpperCase().slice(-3) : '100';
|
|
60
|
+
put(g, 0, 0, 'P100', 'w');
|
|
61
|
+
put(g, 0, 6, 'AITEXT ' + searching, t < 0.9 ? 'g' : 'w');
|
|
62
|
+
put(g, 0, 18, 'Thu 09 Oct', 'w');
|
|
63
|
+
put(g, 0, 31, `10:${String(mn).padStart(2, '0')}/${String(sec).padStart(2, '0')}`, 'y');
|
|
64
|
+
|
|
65
|
+
// Rows 1-3: blue band with mosaic masthead
|
|
66
|
+
for (let r = 1; r <= 3; r++) band(g, r, 0, 39, 'b');
|
|
67
|
+
mosaic(g, 1, 1, 28, 3, 'b', (c) => {
|
|
68
|
+
const GL = {
|
|
69
|
+
A: ['..XXXX..', '.XXXXXX.', 'XX....XX', 'XX....XX', 'XXXXXXXX', 'XXXXXXXX', 'XX....XX', 'XX....XX', 'XX....XX'],
|
|
70
|
+
I: ['XX', 'XX', 'XX', 'XX', 'XX', 'XX', 'XX', 'XX', 'XX'],
|
|
71
|
+
N: ['XX....XX', 'XXX...XX', 'XXXX..XX', 'XXXX..XX', 'XX.XX.XX', 'XX..XXXX', 'XX..XXXX', 'XX...XXX', 'XX....XX'],
|
|
72
|
+
E: ['XXXXXXX', 'XXXXXXX', 'XX.....', 'XX.....', 'XXXXXX.', 'XXXXXX.', 'XX.....', 'XXXXXXX', 'XXXXXXX'],
|
|
73
|
+
W: ['XX......XX', 'XX......XX', 'XX......XX', 'XX..XX..XX', 'XX..XX..XX', 'XX.XXXX.XX', 'XXXX..XXXX', 'XXX....XXX', 'XX......XX'],
|
|
74
|
+
S: ['.XXXXXX', 'XXXXXXX', 'XX.....', 'XXXXXX.', '.XXXXXX', '.....XX', '.....XX', 'XXXXXXX', 'XXXXXX.'],
|
|
75
|
+
};
|
|
76
|
+
let x = 0;
|
|
77
|
+
for (const ch of 'AI NEWS') {
|
|
78
|
+
if (ch === ' ') { x += 4; continue; }
|
|
79
|
+
const gl = GL[ch];
|
|
80
|
+
c.fillStyle = PAL.y;
|
|
81
|
+
gl.forEach((row, j) => { for (let i = 0; i < row.length; i++) if (row[i] === 'X') c.fillRect(x + i, j, 1, 1); });
|
|
82
|
+
x += gl[0].length + 2;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
// red box on the right of the masthead
|
|
86
|
+
for (let r = 1; r <= 3; r++) band(g, r, 30, 39, 'r');
|
|
87
|
+
put(g, 1, 31, 'TOP', 'w', 'r', 0);
|
|
88
|
+
put(g, 2, 31, 'STORIES', 'y', 'r', 1);
|
|
89
|
+
|
|
90
|
+
// Double-height headline
|
|
91
|
+
put(g, 5, 1, 'AI LEARNS TO DREAM', 'y', null, 1);
|
|
92
|
+
// body (cyan / white)
|
|
93
|
+
const body = ['Researchers say the new', 'model now writes poems,', 'plans holidays and', 'tells jokes nobody', 'laughs at.'];
|
|
94
|
+
body.forEach((l, i) => put(g, 8 + i, 1, l, i < 2 ? 'w' : 'w'));
|
|
95
|
+
put(g, 14, 1, 'Experts urge calm', 'c');
|
|
96
|
+
put(g, 14, 21, '102', 'w');
|
|
97
|
+
|
|
98
|
+
// robot illustration (mosaic) on the right
|
|
99
|
+
mosaic(g, 25, 5, 14, 12, 'k', (c) => {
|
|
100
|
+
const R = (x, y, w, h, col) => { c.fillStyle = col; c.fillRect(x, y, w, h); };
|
|
101
|
+
// antenna
|
|
102
|
+
R(13, 2, 2, 5, PAL.w); c.fillStyle = PAL.r; c.beginPath(); c.arc(14, 2.2, 2.2, 0, 7); c.fill();
|
|
103
|
+
// head
|
|
104
|
+
c.fillStyle = PAL.w; c.beginPath(); c.roundRect(4, 7, 20, 16, 3); c.fill();
|
|
105
|
+
// ears
|
|
106
|
+
R(1, 11, 3, 7, PAL.m); R(24, 11, 3, 7, PAL.m);
|
|
107
|
+
// eyes
|
|
108
|
+
R(8, 11, 4, 4, PAL.c); R(16, 11, 4, 4, PAL.c);
|
|
109
|
+
// mouth
|
|
110
|
+
R(8, 18, 12, 2, PAL.r);
|
|
111
|
+
// neck
|
|
112
|
+
R(11, 23, 6, 3, PAL.c);
|
|
113
|
+
// body
|
|
114
|
+
c.fillStyle = PAL.g; c.beginPath(); c.roundRect(2, 26, 24, 10, 3); c.fill();
|
|
115
|
+
R(10, 28, 8, 5, PAL.y);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// robot eyes use the teletext Flash attribute
|
|
119
|
+
for (let r = 8; r <= 9; r++) for (let cc = 25; cc < 39; cc++) if (g[r][cc].mos && g[r][cc].fg === 'c') g[r][cc].flash = true;
|
|
120
|
+
|
|
121
|
+
// Index list with page numbers
|
|
122
|
+
const idx = [['Chip demand soars', '104'], ['Robots join union', '105'], ['Cloud forecast', '106'], ['Quiz: human or bot?', '107']];
|
|
123
|
+
idx.forEach(([a, n], i) => { put(g, 17 + i, 1, a, 'c'); put(g, 17 + i, 21, n, 'w'); });
|
|
124
|
+
// little mosaic bar chart (growth) at right, bars separated by a spacing column
|
|
125
|
+
const bars = [3, 5, 4, 7, 9];
|
|
126
|
+
const bc = ['g', 'g', 'y', 'g', 'y'];
|
|
127
|
+
bars.forEach((hgt, i) => {
|
|
128
|
+
const grow = H.clamp((t - 2.0) * 1.5 - i * 0.14, 0, 1);
|
|
129
|
+
const hh = Math.round(hgt * grow * 1.1);
|
|
130
|
+
for (let k = 0; k < 12; k++) {
|
|
131
|
+
const row = 20 - Math.floor(k / 3), j = 2 - (k % 3);
|
|
132
|
+
if (k < hh) for (let cc = 0; cc < 1; cc++) {
|
|
133
|
+
const cell = g[row][27 + i * 2]; cell.fg = bc[i]; cell.mos |= 3 << (j * 2); cell.ch = ' ';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
put(g, 21, 26, 'GPU INDEX', 'w');
|
|
138
|
+
|
|
139
|
+
// Ticker band
|
|
140
|
+
band(g, 22, 0, 39, 'c');
|
|
141
|
+
if (Math.floor(t * 1.5) % 3 !== 2) put(g, 22, 1, 'LATEST', 'r', 'c');
|
|
142
|
+
const tick = ' Talking toaster seeks agent * Chatbot passes driving test * ';
|
|
143
|
+
const off = Math.floor(t * 6) % tick.length;
|
|
144
|
+
const vis = (tick + tick).substr(off, 31);
|
|
145
|
+
put(g, 22, 8, vis, 'b', 'c');
|
|
146
|
+
|
|
147
|
+
// Fastext row
|
|
148
|
+
put(g, 24, 0, 'Headlines', 'r'); put(g, 24, 11, 'Sport', 'g'); put(g, 24, 19, 'Weather', 'y'); put(g, 24, 29, 'AI Index', 'c');
|
|
149
|
+
return g;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// =====================================================================================
|
|
153
|
+
// EXPLAINER: "How does AI learn?" told as a run of teletext pages (P100 -> P104).
|
|
154
|
+
// Transition = real page change: header searches in green while the old page stays, then the new page
|
|
155
|
+
// overwrites it top-down two rows at a time.
|
|
156
|
+
// =====================================================================================
|
|
157
|
+
|
|
158
|
+
// =====================================================================================
|
|
159
|
+
// Shared rendering: SAA5050-style rounded glyphs (cached bitmaps) and the CRT presentation.
|
|
160
|
+
// =====================================================================================
|
|
161
|
+
const TTC = { key: '', glyph: new Map(), mask: null, face: null, scratch: {} };
|
|
162
|
+
const ttCache = (W, H_) => { const k = W + 'x' + H_; if (TTC.key !== k) { TTC.key = k; TTC.glyph = new Map(); TTC.mask = null; TTC.face = null; TTC.scratch = {}; } };
|
|
163
|
+
const scratch = (name, W, H_) => { let L = TTC.scratch[name]; if (!L) L = TTC.scratch[name] = H.layer(W, H_); const c = L.getContext('2d'); c.setTransform(1, 0, 0, 1, 0, 0); c.globalAlpha = 1; c.globalCompositeOperation = 'source-over'; c.filter = 'none'; c.clearRect(0, 0, W, H_); return L; };
|
|
164
|
+
// glyph metrics (same fit as before: the VT323 'H' fills 80% of the cell width and 74% of the row height)
|
|
165
|
+
const ttMetrics = (cw, rh) => {
|
|
166
|
+
if (TTC.met) { const m = TTC.met; if (m.cw === cw && m.rh === rh) return m; }
|
|
167
|
+
const c = H.layer(8, 8).getContext('2d'); c.font = '400 33px "VT323"';
|
|
168
|
+
const mH = c.measureText('H'), gw = mH.actualBoundingBoxRight + mH.actualBoundingBoxLeft;
|
|
169
|
+
const sx = (cw * 0.8) / gw;
|
|
170
|
+
return (TTC.met = { cw, rh, sx, gx0: cw * 0.09 + mH.actualBoundingBoxLeft * sx, sy: (rh * 0.74) / mH.actualBoundingBoxAscent });
|
|
171
|
+
};
|
|
172
|
+
// One glyph rasterised once at 2x, blurred and re-thresholded: the even, rounded strokes of the SAA5050's
|
|
173
|
+
// character-rounding instead of the font's lumpy pixel steps. Coloured copies are cached too.
|
|
174
|
+
function glyphImg(ch, fg, dh, dw, cw, rh) {
|
|
175
|
+
const key = ch + '|' + fg + '|' + dh + '|' + dw; let gI = TTC.glyph.get(key); if (gI) return gI;
|
|
176
|
+
const mkey = ch + '|' + dh + '|' + dw; let mI = TTC.glyph.get(mkey);
|
|
177
|
+
if (!mI) {
|
|
178
|
+
const { sx, sy, gx0 } = ttMetrics(cw, rh), SC = 2, wc = dw ? 2 : 1, hc = dh ? 2 : 1;
|
|
179
|
+
const px = Math.ceil(cw * 0.35), py = Math.ceil(rh * 0.55), bw = Math.ceil(cw * wc + px * 2), bh = Math.ceil(rh * hc + py * 2);
|
|
180
|
+
const A = H.layer(bw * SC, bh * SC), a = A.getContext('2d');
|
|
181
|
+
a.scale(SC, SC); a.translate(px + gx0 * wc, py + rh * (dh ? 1.6 : 0.8)); a.scale(sx * wc, sy * hc);
|
|
182
|
+
a.font = '400 33px "VT323"'; a.textBaseline = 'alphabetic'; a.fillStyle = '#fff';
|
|
183
|
+
a.fillText(ch, 0, 0); a.fillText(ch, 1.1 / sx, 0);
|
|
184
|
+
const B = H.layer(bw * SC, bh * SC), b = B.getContext('2d', { willReadFrequently: true });
|
|
185
|
+
b.filter = 'blur(2.4px)'; b.drawImage(A, 0, 0); b.filter = 'none';
|
|
186
|
+
const id = b.getImageData(0, 0, B.width, B.height), d = id.data;
|
|
187
|
+
for (let i = 0; i < d.length; i += 4) { const v = H.clamp((d[i + 3] / 255 - 0.27) / 0.22, 0, 1); d[i] = d[i + 1] = d[i + 2] = 255; d[i + 3] = Math.round(v * v * (3 - 2 * v) * 255); }
|
|
188
|
+
b.putImageData(id, 0, 0);
|
|
189
|
+
mI = { img: B, px, py, bw, bh }; TTC.glyph.set(mkey, mI);
|
|
190
|
+
}
|
|
191
|
+
const C = H.layer(mI.img.width, mI.img.height), cc = C.getContext('2d');
|
|
192
|
+
cc.drawImage(mI.img, 0, 0); cc.globalCompositeOperation = 'source-in'; cc.fillStyle = PAL[fg]; cc.fillRect(0, 0, C.width, C.height);
|
|
193
|
+
gI = { img: C, px: mI.px, py: mI.py, bw: mI.bw, bh: mI.bh }; TTC.glyph.set(key, gI); return gI;
|
|
194
|
+
}
|
|
195
|
+
function drawGlyph(c, cell, x, y, cw, rh) {
|
|
196
|
+
const gI = glyphImg(cell.ch, cell.fg, cell.dh === 1 ? 1 : 0, cell.dw ? 1 : 0, cw, rh);
|
|
197
|
+
c.drawImage(gI.img, x - gI.px, y - gI.py, gI.bw, gI.bh);
|
|
198
|
+
}
|
|
199
|
+
// Static screen overlays, cached per size:
|
|
200
|
+
// - shade: one multiply layer = phosphor shadow mask (aperture-grille RGB triads with staggered slot breaks)
|
|
201
|
+
// x scanlines x vignette x the curved tube's inner edge shadow and darker corners
|
|
202
|
+
// - glass: a faint diagonal reflection and dome highlight on the tube face
|
|
203
|
+
const rrPath = (f, x, y, w, h, r) => { f.moveTo(x + r, y); f.arcTo(x + w, y, x + w, y + h, r); f.arcTo(x + w, y + h, x, y + h, r); f.arcTo(x, y + h, x, y, r); f.arcTo(x, y, x + w, y, r); f.closePath(); };
|
|
204
|
+
const screenOverlays = (W, H_) => {
|
|
205
|
+
if (TTC.face) return TTC.face;
|
|
206
|
+
const SH = H.layer(W, H_), f = SH.getContext('2d');
|
|
207
|
+
f.fillStyle = '#fff'; f.fillRect(0, 0, W, H_);
|
|
208
|
+
const T = H.layer(6, 6), tc = T.getContext('2d');
|
|
209
|
+
const cols = ['#ff7a7a', '#7aff7a', '#7a7aff'];
|
|
210
|
+
for (let x = 0; x < 6; x++) { tc.fillStyle = cols[x % 3]; tc.fillRect(x, 0, 1, 6); }
|
|
211
|
+
tc.fillStyle = 'rgba(0,0,0,0.35)'; tc.fillRect(0, 2, 3, 1); tc.fillRect(3, 5, 3, 1);
|
|
212
|
+
f.save(); f.globalCompositeOperation = 'multiply'; f.globalAlpha = 0.22; f.fillStyle = f.createPattern(T, 'repeat'); f.fillRect(0, 0, W, H_); f.restore();
|
|
213
|
+
f.fillStyle = 'rgba(0,0,0,0.28)'; for (let y = 0; y < H_; y += 3) f.fillRect(0, y + 2, W, 1);
|
|
214
|
+
H.vignette(f, W, H_, 0.42);
|
|
215
|
+
f.save(); f.beginPath(); f.rect(-200, -200, W + 400, H_ + 400); rrPath(f, 10, 8, W - 20, H_ - 16, 54);
|
|
216
|
+
f.shadowColor = 'rgba(0,0,0,0.85)'; f.shadowBlur = 36; f.fillStyle = '#000'; f.fill('evenodd'); f.restore();
|
|
217
|
+
f.beginPath(); f.rect(-10, -10, W + 20, H_ + 20); rrPath(f, 3, 2, W - 6, H_ - 4, 60); f.fillStyle = '#000'; f.fill('evenodd');
|
|
218
|
+
const GL = H.layer(W, H_), g = GL.getContext('2d');
|
|
219
|
+
const gl = g.createLinearGradient(W * 0.05, 0, W * 0.42, H_ * 0.75);
|
|
220
|
+
gl.addColorStop(0, 'rgba(255,255,255,0)'); gl.addColorStop(0.42, 'rgba(225,235,255,0.045)'); gl.addColorStop(0.55, 'rgba(225,235,255,0.012)'); gl.addColorStop(1, 'rgba(255,255,255,0)');
|
|
221
|
+
g.fillStyle = gl; g.beginPath(); rrPath(g, 10, 8, W - 20, H_ - 16, 54); g.fill();
|
|
222
|
+
const dome = g.createRadialGradient(W * 0.3, H_ * 0.18, 0, W * 0.3, H_ * 0.18, W * 0.45);
|
|
223
|
+
dome.addColorStop(0, 'rgba(210,225,255,0.035)'); dome.addColorStop(1, 'rgba(210,225,255,0)');
|
|
224
|
+
g.fillStyle = dome; g.beginPath(); rrPath(g, 10, 8, W - 20, H_ - 16, 54); g.fill();
|
|
225
|
+
return (TTC.face = { shade: SH, glass: GL });
|
|
226
|
+
};
|
|
227
|
+
// CRT presentation of a rendered page layer L (shared by the still and every explainer page)
|
|
228
|
+
function crtPresent(ctx, W, H_, L, t) {
|
|
229
|
+
const MX = 30, MY = 16, PW = W - MX * 2, PH = H_ - MY * 2; // overscan margin
|
|
230
|
+
const OV = screenOverlays(W, H_);
|
|
231
|
+
// soft-focus picture
|
|
232
|
+
const S = scratch('S', W, H_), sc = S.getContext('2d');
|
|
233
|
+
sc.fillStyle = '#000'; sc.fillRect(0, 0, W, H_);
|
|
234
|
+
sc.filter = 'blur(0.6px)'; sc.drawImage(L, MX, MY, PW, PH); sc.filter = 'none';
|
|
235
|
+
// red gun lands off-convergence: lift the red channel out and put it back 1.4px to the right
|
|
236
|
+
// (real red/cyan colour fringes instead of the old white ghost)
|
|
237
|
+
const Rg = scratch('R', W, H_), rc = Rg.getContext('2d');
|
|
238
|
+
rc.drawImage(S, 0, 0); rc.globalCompositeOperation = 'multiply'; rc.fillStyle = '#ff0000'; rc.fillRect(0, 0, W, H_);
|
|
239
|
+
ctx.drawImage(S, 0, 0);
|
|
240
|
+
ctx.save(); ctx.globalCompositeOperation = 'difference'; ctx.drawImage(Rg, 0, 0);
|
|
241
|
+
ctx.globalCompositeOperation = 'lighter'; ctx.drawImage(Rg, 1.4, 0);
|
|
242
|
+
// tube glass: blacks sit slightly above #000
|
|
243
|
+
ctx.fillStyle = 'rgb(9,10,12)'; ctx.fillRect(0, 0, W, H_);
|
|
244
|
+
ctx.restore();
|
|
245
|
+
// phosphor bloom
|
|
246
|
+
const B = scratch('bloom', W / 4, H_ / 4), bc = B.getContext('2d');
|
|
247
|
+
bc.filter = 'blur(3px)'; bc.drawImage(L, MX / 4, MY / 4, PW / 4, PH / 4); bc.filter = 'none';
|
|
248
|
+
ctx.save(); ctx.globalCompositeOperation = 'screen'; ctx.globalAlpha = 0.5; ctx.drawImage(B, 0, 0, W, H_); ctx.restore();
|
|
249
|
+
// shadow mask, scanlines, vignette and the tube's edge shadow in one multiply
|
|
250
|
+
ctx.save(); ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(OV.shade, 0, 0); ctx.restore();
|
|
251
|
+
// slow CRT refresh roll
|
|
252
|
+
const ry = ((t * 0.35) % 1) * (H_ + 200) - 100;
|
|
253
|
+
const rg = ctx.createLinearGradient(0, ry - 100, 0, ry + 100);
|
|
254
|
+
rg.addColorStop(0, 'rgba(255,255,255,0)'); rg.addColorStop(0.5, 'rgba(255,255,255,0.035)'); rg.addColorStop(1, 'rgba(255,255,255,0)');
|
|
255
|
+
ctx.fillStyle = rg; ctx.fillRect(0, ry - 100, W, 200);
|
|
256
|
+
ctx.drawImage(OV.glass, 0, 0);
|
|
257
|
+
H.grain(ctx, W, H_, 0.035, 5 + Math.floor(t * 25), true);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function renderTT(ctx, W, H_, g, t) {
|
|
261
|
+
ttCache(W, H_);
|
|
262
|
+
const cw = W / COLS, rh = H_ / ROWS;
|
|
263
|
+
const X = (c) => Math.round(c * cw), Y = (r) => Math.round(r * rh);
|
|
264
|
+
const L = scratch('page', W, H_), c = L.getContext('2d');
|
|
265
|
+
c.fillStyle = '#000'; c.fillRect(0, 0, W, H_);
|
|
266
|
+
const flashOff = Math.floor(t * 3) % 3 === 2;
|
|
267
|
+
for (let r = 0; r < ROWS; r++) for (let col = 0; col < COLS; col++) {
|
|
268
|
+
const cell = g[r][col];
|
|
269
|
+
const x0 = X(col), x1 = X(col + 1), y0 = Y(r), y1 = Y(r + 1);
|
|
270
|
+
c.fillStyle = PAL[cell.bg]; c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
271
|
+
if (cell.mos && !(cell.flash && flashOff)) {
|
|
272
|
+
c.fillStyle = PAL[cell.fg];
|
|
273
|
+
for (let j = 0; j < 3; j++) for (let i = 0; i < 2; i++) {
|
|
274
|
+
if (!(cell.mos & (1 << (j * 2 + i)))) continue;
|
|
275
|
+
const sx0 = Math.round(x0 + (i * (x1 - x0)) / 2), sx1 = Math.round(x0 + ((i + 1) * (x1 - x0)) / 2);
|
|
276
|
+
const sy0 = Math.round(r * rh + (j * rh) / 3), sy1 = Math.round(r * rh + ((j + 1) * rh) / 3);
|
|
277
|
+
c.fillRect(sx0, sy0, sx1 - sx0, sy1 - sy0);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (let r = 0; r < ROWS; r++) for (let col = 0; col < COLS; col++) {
|
|
282
|
+
const cell = g[r][col];
|
|
283
|
+
if (cell.mos || cell.ch === ' ' || cell.dh === 2 || (cell.flash && flashOff)) continue;
|
|
284
|
+
drawGlyph(c, cell, X(col), Y(r), cw, rh);
|
|
285
|
+
}
|
|
286
|
+
crtPresent(ctx, W, H_, L, t);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// double-size (Level 2.5 style): each character spans 2 columns x 2 rows
|
|
290
|
+
function put2(g, row, col, str, fg = 'w', bg = null) {
|
|
291
|
+
for (let i = 0; i < str.length && col + i * 2 + 1 < COLS; i++) {
|
|
292
|
+
const c0 = col + i * 2;
|
|
293
|
+
for (const [r, c, k] of [[row, c0, 1], [row, c0 + 1, 2], [row + 1, c0, 2], [row + 1, c0 + 1, 2]]) {
|
|
294
|
+
const cell = g[r][c]; cell.mos = 0; if (bg) cell.bg = bg;
|
|
295
|
+
if (k === 1) { cell.ch = str[i]; cell.fg = fg; cell.dh = 1; cell.dw = 1; } else { cell.ch = ' '; cell.dh = 2; cell.dw = 0; }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const putC = (g, row, str, fg, bg, dh) => put(g, row, Math.max(0, Math.floor((COLS - str.length) / 2)), str, fg, bg, dh);
|
|
300
|
+
const flashCells = (g, r0, r1, c0, c1) => { for (let r = r0; r <= r1; r++) for (let c = c0; c <= c1; c++) g[r][c].flash = true; };
|
|
301
|
+
const fmtN = (n) => Math.round(n).toLocaleString('en-US');
|
|
302
|
+
// mosaic pictures (drawn in sub-pixel units: 2 per cell across, 3 per cell down)
|
|
303
|
+
const catPic = (col) => (c, w, h) => {
|
|
304
|
+
c.fillStyle = PAL[col];
|
|
305
|
+
c.beginPath(); c.moveTo(w * 0.14, h * 0.05); c.lineTo(w * 0.42, h * 0.32); c.lineTo(w * 0.1, h * 0.45); c.closePath(); c.fill();
|
|
306
|
+
c.beginPath(); c.moveTo(w * 0.86, h * 0.05); c.lineTo(w * 0.58, h * 0.32); c.lineTo(w * 0.9, h * 0.45); c.closePath(); c.fill();
|
|
307
|
+
c.beginPath(); c.ellipse(w * 0.5, h * 0.58, w * 0.42, h * 0.4, 0, 0, 7); c.fill();
|
|
308
|
+
c.fillStyle = '#000'; c.fillRect(w * 0.27, h * 0.48, w * 0.13, h * 0.14); c.fillRect(w * 0.6, h * 0.48, w * 0.13, h * 0.14);
|
|
309
|
+
c.fillStyle = PAL.r; c.fillRect(w * 0.43, h * 0.68, w * 0.14, h * 0.1);
|
|
310
|
+
};
|
|
311
|
+
const dogPic = (col) => (c, w, h) => {
|
|
312
|
+
c.fillStyle = PAL[col];
|
|
313
|
+
c.beginPath(); c.ellipse(w * 0.5, h * 0.5, w * 0.32, h * 0.44, 0, 0, 7); c.fill();
|
|
314
|
+
c.beginPath(); c.ellipse(w * 0.14, h * 0.46, w * 0.12, h * 0.34, 0.25, 0, 7); c.fill();
|
|
315
|
+
c.beginPath(); c.ellipse(w * 0.86, h * 0.46, w * 0.12, h * 0.34, -0.25, 0, 7); c.fill();
|
|
316
|
+
c.fillStyle = '#000'; c.fillRect(w * 0.33, h * 0.36, w * 0.11, h * 0.13); c.fillRect(w * 0.56, h * 0.36, w * 0.11, h * 0.13);
|
|
317
|
+
c.fillRect(w * 0.42, h * 0.62, w * 0.16, h * 0.13);
|
|
318
|
+
};
|
|
319
|
+
const robotPic = (c) => {
|
|
320
|
+
const R = (x, y, w, h, col) => { c.fillStyle = col; c.fillRect(x, y, w, h); };
|
|
321
|
+
R(11, 0, 2, 4, PAL.w); c.fillStyle = PAL.r; c.beginPath(); c.arc(12, 1.2, 1.8, 0, 7); c.fill();
|
|
322
|
+
c.fillStyle = PAL.w; c.beginPath(); c.roundRect(3, 4, 18, 14, 3); c.fill();
|
|
323
|
+
R(0, 8, 3, 6, PAL.m); R(21, 8, 3, 6, PAL.m);
|
|
324
|
+
R(6, 7, 4, 4, PAL.c); R(14, 7, 4, 4, PAL.c);
|
|
325
|
+
R(7, 13, 10, 2, PAL.r);
|
|
326
|
+
};
|
|
327
|
+
const arrowPic = (col, len, dir = 1) => (c, w, h) => { // horizontal arrow; len 0..1 draws on from the tail
|
|
328
|
+
c.fillStyle = PAL[col];
|
|
329
|
+
const L = w * len;
|
|
330
|
+
if (dir > 0) { c.fillRect(0, h * 0.34, Math.max(0, L - h * 0.6), h * 0.32); if (len > 0.6) { c.beginPath(); c.moveTo(L - h * 0.9, 0); c.lineTo(L, h / 2); c.lineTo(L - h * 0.9, h); c.closePath(); c.fill(); } }
|
|
331
|
+
else { c.fillRect(w - L + h * 0.6, h * 0.34, Math.max(0, L - h * 0.6), h * 0.32); if (len > 0.6) { c.beginPath(); c.moveTo(w - L + h * 0.9, 0); c.lineTo(w - L, h / 2); c.lineTo(w - L + h * 0.9, h); c.closePath(); c.fill(); } }
|
|
332
|
+
};
|
|
333
|
+
const tickPic = (c, w, h) => { c.strokeStyle = PAL.g; c.lineWidth = 2.4; c.lineCap = 'square'; c.beginPath(); c.moveTo(w * 0.1, h * 0.55); c.lineTo(w * 0.4, h * 0.85); c.lineTo(w * 0.9, h * 0.12); c.stroke(); };
|
|
334
|
+
const crossPic = (c, w, h) => { c.strokeStyle = PAL.r; c.lineWidth = 2.6; c.beginPath(); c.moveTo(w * 0.12, h * 0.1); c.lineTo(w * 0.88, h * 0.9); c.moveTo(w * 0.88, h * 0.1); c.lineTo(w * 0.12, h * 0.9); c.stroke(); };
|
|
335
|
+
|
|
336
|
+
function exHeader(g, num, t, searching) {
|
|
337
|
+
const sec = (12 + Math.floor(t)) % 60;
|
|
338
|
+
put(g, 0, 0, 'P' + num, 'w');
|
|
339
|
+
const roll = searching ? (Math.floor(t * 24) * 37 % 899 + 100).toString(16).toUpperCase().slice(-3) : num;
|
|
340
|
+
put(g, 0, 6, 'AITEXT ' + roll, searching ? 'g' : 'w');
|
|
341
|
+
put(g, 0, 18, 'Thu 09 Oct', 'w');
|
|
342
|
+
put(g, 0, 31, `10:24/${String(sec).padStart(2, '0')}`, 'y');
|
|
343
|
+
}
|
|
344
|
+
function stepBand(g, step) {
|
|
345
|
+
band(g, 1, 0, 39, 'b');
|
|
346
|
+
put(g, 1, 1, 'HOW DOES AI LEARN?', 'y', 'b');
|
|
347
|
+
put(g, 1, 30, 'STEP ' + step + '/4', 'c', 'b');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ---- pages (tl = time since this page was requested) ----
|
|
351
|
+
function page100(g, tl) {
|
|
352
|
+
for (let r = 1; r <= 3; r++) band(g, r, 0, 39, 'b');
|
|
353
|
+
put(g, 2, 2, 'AITEXT SCIENCE EXPLAINED', 'w', 'b');
|
|
354
|
+
put(g, 6, 3, 'HOW DOES', 'y', null, 1);
|
|
355
|
+
put(g, 9, 3, 'AI LEARN?', 'y', null, 1);
|
|
356
|
+
put(g, 13, 3, 'A guide in four', 'c');
|
|
357
|
+
put(g, 14, 3, 'short pages.', 'c');
|
|
358
|
+
mosaic(g, 26, 5, 12, 12, 'k', (c, w, h) => {
|
|
359
|
+
c.fillStyle = PAL.m; c.lineWidth = 6; c.strokeStyle = PAL.m; c.lineCap = 'butt';
|
|
360
|
+
c.beginPath(); c.arc(w / 2, h * 0.3, w * 0.28, Math.PI * 1.05, Math.PI * 2.25); c.stroke();
|
|
361
|
+
c.fillRect(w / 2 - 3, h * 0.42, 6, h * 0.22); c.fillRect(w / 2 - 3, h * 0.78, 6, h * 0.14);
|
|
362
|
+
});
|
|
363
|
+
flashCells(g, 15, 16, 26, 37);
|
|
364
|
+
put(g, 20, 3, 'Page through: 101 102 103 104', 'w');
|
|
365
|
+
put(g, 24, 0, 'Next page', 'r'); put(g, 24, 13, 'Index', 'g'); put(g, 24, 22, 'Quiz', 'y'); put(g, 24, 31, 'Science', 'c');
|
|
366
|
+
}
|
|
367
|
+
function page101(g, tl) {
|
|
368
|
+
stepBand(g, 1);
|
|
369
|
+
put(g, 3, 1, 'TRAINING DATA', 'c', null, 1);
|
|
370
|
+
put(g, 3, 24, 'LABELLED', 'w', null, 1);
|
|
371
|
+
// four labelled examples appear one by one
|
|
372
|
+
const ex = [[1, 6, 'cat', 'y'], [9, 6, 'dog', 'w'], [1, 13, 'dog', 'w'], [9, 13, 'cat', 'y']];
|
|
373
|
+
ex.forEach(([c0, r0, lab, col], i) => {
|
|
374
|
+
if (tl < 0.25 + i * 0.22) return;
|
|
375
|
+
mosaic(g, c0, r0, 6, 5, 'k', (lab === 'cat' ? catPic : dogPic)(col));
|
|
376
|
+
put(g, r0 + 5, c0 + 1, ' ' + lab + ' ', 'b', lab === 'cat' ? 'y' : 'c'); // label tag under its picture
|
|
377
|
+
});
|
|
378
|
+
// arrow draws on, then examples stream along it into the model
|
|
379
|
+
const al = H.clamp((tl - 1.0) / 0.5);
|
|
380
|
+
if (al > 0) mosaic(g, 16, 10, 10, 3, 'k', arrowPic('w', Math.ceil(al * 10) / 10));
|
|
381
|
+
put(g, 9, 16, 'feed in', 'w');
|
|
382
|
+
mosaic(g, 27, 6, 12, 7, 'k', robotPic);
|
|
383
|
+
put(g, 13, 29, 'THE MODEL', 'g');
|
|
384
|
+
if (tl > 1.5) {
|
|
385
|
+
const ph = ((tl - 1.5) / 0.45) % 1, k = Math.floor((tl - 1.5) / 0.45);
|
|
386
|
+
const col = 16 + Math.floor(ph * 8);
|
|
387
|
+
mosaic(g, col, 14, 3, 2, 'k', (k % 2 ? dogPic : catPic)(k % 2 ? 'w' : 'y'));
|
|
388
|
+
put(g, 16, col, k % 2 ? 'dog' : 'cat', k % 2 ? 'w' : 'y');
|
|
389
|
+
}
|
|
390
|
+
// counter
|
|
391
|
+
const n = 1e6 * H.ease.inOut(H.clamp((tl - 1.4) / 2.0));
|
|
392
|
+
if (tl > 1.3) { put2(g, 20, 0, fmtN(n).padStart(9, ' '), 'y'); put(g, 20, 19, ' EXAMPLES', 'w', null, 1); }
|
|
393
|
+
if (tl > 1.3) put(g, 23, 1, 'Every picture comes with its answer.', 'c');
|
|
394
|
+
put(g, 24, 0, 'Back', 'r'); put(g, 24, 13, 'Index', 'g'); put(g, 24, 22, 'Next', 'y'); put(g, 24, 31, 'Science', 'c');
|
|
395
|
+
}
|
|
396
|
+
function page102(g, tl) {
|
|
397
|
+
stepBand(g, 2);
|
|
398
|
+
// the loop strip at the bottom: GUESS -> CHECK -> ADJUST, return arrow
|
|
399
|
+
const step = tl < 0.5 ? -1 : tl < 1.7 ? 0 : tl < 2.5 ? 1 : tl < 3.6 ? 2 : tl < 4.1 ? 0 : 1;
|
|
400
|
+
const boxes = [['GUESS', 'y', 2], ['CHECK', 'c', 15], ['ADJUST', 'm', 28]];
|
|
401
|
+
boxes.forEach(([lab, col, c0], i) => {
|
|
402
|
+
const act = i === step;
|
|
403
|
+
band(g, 20, c0, c0 + 9, act ? col : 'k'); band(g, 21, c0, c0 + 9, act ? col : 'k');
|
|
404
|
+
put(g, 20, c0 + 1, lab, act ? 'b' : col, act ? col : 'k', 1);
|
|
405
|
+
});
|
|
406
|
+
put(g, 20, 12, '>', 'w', null, 1); put(g, 20, 25, '>', 'w', null, 1);
|
|
407
|
+
mosaic(g, 3, 22, 32, 1, 'k', (c, w, h) => { c.fillStyle = PAL.w; c.fillRect(1, 0, 2, 2); c.fillRect(1, 1, w - 4, 1.2); c.fillRect(w - 3, 0, 2, 2); c.beginPath(); c.moveTo(0, 0); c.lineTo(4, 0); c.lineTo(2, -2); c.fill(); });
|
|
408
|
+
put(g, 23, 12, 'repeat, repeat, repeat', 'w');
|
|
409
|
+
// new picture -> model
|
|
410
|
+
mosaic(g, 1, 4, 8, 6, 'k', catPic('y'));
|
|
411
|
+
put(g, 10, 1, 'NEW PIC', 'c');
|
|
412
|
+
const a1 = H.clamp((tl - 0.3) / 0.4);
|
|
413
|
+
if (a1 > 0) mosaic(g, 9, 6, 5, 2, 'k', arrowPic('w', a1));
|
|
414
|
+
mosaic(g, 14, 4, 12, 7, 'k', robotPic);
|
|
415
|
+
// weights: three mosaic sliders under the robot whose knobs step when adjusting
|
|
416
|
+
const adj = H.clamp((tl - 2.8) / 0.7);
|
|
417
|
+
const knobs = [[0.2, 0.75], [0.7, 0.3], [0.45, 0.85]];
|
|
418
|
+
put(g, 11, 14, 'weights', 'm');
|
|
419
|
+
knobs.forEach(([a, b], i) => {
|
|
420
|
+
const v = H.lerp(a, b, Math.floor(adj * 5) / 5);
|
|
421
|
+
mosaic(g, 14, 12 + i, 12, 1, 'k', (c, w, h) => { c.fillStyle = PAL.b; c.fillRect(0, 1, w, 1); c.fillStyle = adj > 0 && adj < 1 ? PAL.y : PAL.m; c.fillRect(Math.round(v * (w - 3)), 0, 3, 3); });
|
|
422
|
+
});
|
|
423
|
+
// guess area
|
|
424
|
+
const a2 = H.clamp((tl - 0.9) / 0.3);
|
|
425
|
+
if (a2 > 0) mosaic(g, 26, 6, 3, 2, 'k', arrowPic('w', a2));
|
|
426
|
+
if (tl > 1.1 && tl < 3.6) {
|
|
427
|
+
put(g, 4, 30, 'DOG?', 'r', null, 1);
|
|
428
|
+
if (tl > 1.7) { mosaic(g, 35, 4, 4, 3, 'k', crossPic); flashCells(g, 4, 6, 35, 38); band(g, 4, 29, 34, 'w'); band(g, 5, 29, 34, 'w'); }
|
|
429
|
+
if (tl > 1.9) { put(g, 8, 29, 'NO - IT IS', 'c'); put(g, 9, 29, 'A CAT', 'c'); }
|
|
430
|
+
}
|
|
431
|
+
if (tl > 2.5 && tl < 3.6) {
|
|
432
|
+
// error signal travels back from the guess to the weights (right to left), a block per step
|
|
433
|
+
const e = H.clamp((tl - 2.5) / 0.5);
|
|
434
|
+
mosaic(g, 26, 14, 13, 2, 'k', arrowPic('r', Math.ceil(e * 8) / 8, -1));
|
|
435
|
+
put(g, 16, 30, 'error', 'r');
|
|
436
|
+
}
|
|
437
|
+
if (tl >= 3.6) {
|
|
438
|
+
put(g, 4, 30, 'CAT', 'g', null, 1);
|
|
439
|
+
mosaic(g, 35, 4, 4, 3, 'k', tickPic);
|
|
440
|
+
put(g, 8, 29, 'CORRECT!', 'g');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function page103(g, tl) {
|
|
444
|
+
stepBand(g, 3);
|
|
445
|
+
put(g, 3, 1, 'ACCURACY', 'w', null, 1);
|
|
446
|
+
const pc = Math.round(H.lerp(61, 97, H.ease.inOut(H.clamp((tl - 0.9) / 1.2))));
|
|
447
|
+
const done = tl > 2.15;
|
|
448
|
+
put2(g, 3, 11, ' ' + pc + '% ', done ? 'b' : 'y', done ? 'y' : null);
|
|
449
|
+
if (done) flashCells(g, 3, 4, 11, 20);
|
|
450
|
+
put(g, 3, 23, 'after', 'c'); put(g, 4, 23, 'each round', 'c');
|
|
451
|
+
// bar chart: one bar per training round, stepped in
|
|
452
|
+
const nb = Math.min(12, Math.floor(H.clamp((tl - 0.85) / 1.3) * 12) + (tl > 0.85 ? 1 : 0));
|
|
453
|
+
for (let i = 0; i < nb; i++) {
|
|
454
|
+
const v = H.lerp(61, 97, 1 - Math.pow(1 - i / 11, 2));
|
|
455
|
+
const hh = Math.round(((v - 50) / 50) * 36); // sub-rows (12 rows * 3)
|
|
456
|
+
for (let k = 0; k < hh; k++) {
|
|
457
|
+
const row = 18 - Math.floor(k / 3), j = 2 - (k % 3);
|
|
458
|
+
const cell = g[row][3 + i * 2]; cell.fg = i === nb - 1 ? 'y' : 'g'; cell.mos |= 3 << (j * 2); cell.ch = ' ';
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
mosaic(g, 2, 19, 25, 1, 'k', (c, w) => { c.fillStyle = PAL.w; c.fillRect(0, 0, w, 1.2); });
|
|
462
|
+
put(g, 20, 3, 'TRAINING ROUNDS >>>', 'c');
|
|
463
|
+
if (tl > 0.9) { put(g, 16, 28, '61% at', 'w'); put(g, 17, 28, 'round 1', 'w'); }
|
|
464
|
+
if (done) { put(g, 12, 28, '97% at', 'y'); put(g, 13, 28, 'round 12', 'y'); }
|
|
465
|
+
}
|
|
466
|
+
function page104(g, tl) {
|
|
467
|
+
stepBand(g, 4);
|
|
468
|
+
putC(g, 5, 'THE TAKEAWAY', 'c');
|
|
469
|
+
put(g, 8, 3, 'MORE EXAMPLES', 'w', null, 1);
|
|
470
|
+
mosaic(g, 18, 10, 6, 3, 'k', (c, w, h) => { c.fillStyle = PAL.y; c.fillRect(w * 0.42, 0, w * 0.16, h * 0.55); c.beginPath(); c.moveTo(0, h * 0.45); c.lineTo(w, h * 0.45); c.lineTo(w / 2, h); c.fill(); });
|
|
471
|
+
put(g, 14, 13, 'BETTER GUESSES', 'y', null, 1);
|
|
472
|
+
// rising staircase of blocks
|
|
473
|
+
for (let i = 0; i < 6; i++) { if (tl < 0.4 + i * 0.08) continue; for (let k = 0; k <= i; k++) for (let cc = 0; cc < 4; cc++) { const cell = g[22 - k][3 + i * 6 + cc]; cell.mos = 63; cell.fg = i === 5 ? 'y' : 'g'; } }
|
|
474
|
+
put(g, 24, 0, 'Index', 'r'); put(g, 24, 13, 'Quiz', 'g'); put(g, 24, 22, 'Again', 'y'); put(g, 24, 31, 'Science', 'c');
|
|
475
|
+
}
|
|
476
|
+
const EX_PAGES = [[0, '100', page100], [1.85, '101', page101], [5.55, '102', page102], [10.3, '103', page103], [12.75, '104', page104]];
|
|
477
|
+
function buildEx(i, t) {
|
|
478
|
+
const g = newGrid();
|
|
479
|
+
const [t0, num, fn] = EX_PAGES[i];
|
|
480
|
+
fn(g, t - t0);
|
|
481
|
+
exHeader(g, num, t, false);
|
|
482
|
+
return g;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ---- SOUND: a TV set showing teletext. Mains hum + room, a soft library-music pad (like the pages-from-teletext
|
|
486
|
+
// filler music), a remote-control button press on every page request, a quiet seek chatter while the green header
|
|
487
|
+
// counter rolls, a "found" bleep, then a fast stepped data wipe as rows land two at a time. Everything on the page is
|
|
488
|
+
// a soft lowpassed square bleep (a small TV speaker). Times copy EX_PAGES / SEARCH = 0.35 / 22 row-pairs per second.
|
|
489
|
+
function ttSound(k) {
|
|
490
|
+
const END = 14.8, SEARCH = 0.35;
|
|
491
|
+
const P = EX_PAGES.map((p) => p[0]); // [0, 1.85, 5.55, 10.3, 12.75]
|
|
492
|
+
const bleep = (t, f, d = 0.07, g = 1, pan = 0, minor = false) => k.tone(t, { f, type: 'square', dur: d, gain: 0.075 * g, a: 0.002, hold: d * 0.6, filter: 'lowpass', f2: 1800, pan, minor });
|
|
493
|
+
const remote = (t) => { k.noise(t, { dur: 0.02, filter: 'bandpass', f: 1400, q: 1.5, gain: 0.32, a: 0.001 }); k.tone(t, { f: [[0, 190], [0.03, 120]], dur: 0.045, gain: 0.18 }); };
|
|
494
|
+
// row-pair wipe: one soft data click per two rows landing, top to bottom (pitch steps down the page)
|
|
495
|
+
const wipe = (t0) => { for (let i = 0; i < 12; i++) k.noise(t0 + i / 22, { dur: 0.03, filter: 'bandpass', f: 2000 - i * 90, q: 2.5, gain: 0.16 * (1 - i / 20), a: 0.002 }); k.noise(t0, { dur: 0.56, color: 'pink', filter: 'bandpass', f: [[0, 1600], [0.55, 700]], q: 1, gain: 0.035, a: 0.05, hold: 0.4 }); };
|
|
496
|
+
const page = (t0, first) => {
|
|
497
|
+
remote(t0);
|
|
498
|
+
for (let i = 0; i < 4; i++) bleep(t0 + 0.04 + i * 0.075, [330, 294, 370, 262][i], 0.03, 0.55, -0.3, true); // header counter seeks
|
|
499
|
+
bleep(t0 + SEARCH, first ? 'A5' : 'E5', 0.09, 1.3); // page found
|
|
500
|
+
wipe(t0 + SEARCH);
|
|
501
|
+
};
|
|
502
|
+
const counter = (t0, t1, fn, step, play) => { let last = -1, lastV = fn(t0); for (let t = t0; t <= t1 + 1e-6; t += 1 / 120) { const v = fn(t); if (v - lastV >= step && t - last >= 1 / 12) { play(t, v); last = t; lastV = v; } } };
|
|
503
|
+
|
|
504
|
+
// ---- bed + music
|
|
505
|
+
k.bed('hum', 0, END, { gain: 0.4 });
|
|
506
|
+
k.bed('room', 0, END, { gain: 0.3 });
|
|
507
|
+
k.music('pad', 0.4, END, { bpm: 96, gain: 0.32, chords: [['C4', 'E4', 'G4', 'B4'], ['A3', 'C4', 'E4', 'G4'], ['D4', 'F4', 'A4', 'C5'], ['G3', 'B3', 'D4', 'F4']] });
|
|
508
|
+
|
|
509
|
+
// ---- P100: title page
|
|
510
|
+
page(P[0], true);
|
|
511
|
+
// ---- P101: training data
|
|
512
|
+
page(P[1]);
|
|
513
|
+
const L1 = P[1];
|
|
514
|
+
bleep(L1 + 0.69, 'C5', 0.06, 0.8, -0.5, true); bleep(L1 + 0.91, 'E5', 0.06, 0.8, -0.3, true); // last two example pictures appear
|
|
515
|
+
for (let s = 2; s <= 10; s += 2) bleep(L1 + 1.0 + (s / 10) * 0.5 - 0.05, 262 + s * 25, 0.04, 0.6, 0, true); // arrow steps on (10 steps / 0.5 s)
|
|
516
|
+
for (let t = L1 + 1.5; t < P[2] - 0.05; t += 0.45) bleep(t, 'G4', 0.05, 0.6, 0.2, true); // a picture streams out along the arrow
|
|
517
|
+
counter(L1 + 1.4, L1 + 3.4, (t) => H.ease.inOut(H.clamp((t - L1 - 1.4) / 2.0)), 1 / 30, (t) => bleep(t, 220, 0.025, 0.7, -0.4, true)); // 0 -> 1,000,000
|
|
518
|
+
bleep(L1 + 3.4, 'C5', 0.06, 1, -0.4); bleep(L1 + 3.46, 'G5', 0.12, 1, -0.4); // counter lands on 1,000,000
|
|
519
|
+
|
|
520
|
+
// ---- P102: guess / check / adjust
|
|
521
|
+
page(P[2]);
|
|
522
|
+
const L2 = P[2];
|
|
523
|
+
bleep(L2 + 1.1, 'A4', 0.08, 1, 0.5); bleep(L2 + 1.18, 'D5', 0.12, 1, 0.5); // DOG? (rising, a question)
|
|
524
|
+
// CHECK (tl 1.7): wrong. cross + flash + white band: harsh low two-note buzz
|
|
525
|
+
const WRONG = L2 + 1.7;
|
|
526
|
+
k.tone(WRONG, { f: 'Bb2', type: 'square', dur: 0.16, gain: 0.09, hold: 0.12, filter: 'lowpass', f2: 1300, pan: 0.5 });
|
|
527
|
+
k.tone(WRONG + 0.17, { f: 'E2', type: 'square', dur: 0.32, gain: 0.1, hold: 0.24, filter: 'lowpass', f2: 1100, pan: 0.5 });
|
|
528
|
+
k.play('thud', WRONG, { f: 100, gain: 0.45, pan: 0.5 });
|
|
529
|
+
bleep(L2 + 1.9, 'C5', 0.04, 0.6, 0.5, true); // "NO - IT IS A CAT"
|
|
530
|
+
// ADJUST (tl 2.5): error arrow steps back right -> left (8 steps over 0.5 s, one bleep per two)
|
|
531
|
+
for (let s = 1; s <= 8; s += 2) bleep(L2 + 2.5 + (s / 8) * 0.5 - 0.06, 523 - s * 35, 0.05, 0.9, 0.6 - s * 0.12);
|
|
532
|
+
for (let s = 1; s <= 5; s++) bleep(L2 + 2.8 + (s / 5) * 0.7 - 0.14, [196, 220, 247, 262, 294][s - 1], 0.05, 0.8, -0.1, true); // weight knobs step (5 levels)
|
|
533
|
+
// GUESS again -> CAT / CORRECT (tl 3.6)
|
|
534
|
+
['C5', 'E5', 'G5'].forEach((n, i) => bleep(L2 + 3.6 + i * 0.07, n, i === 2 ? 0.25 : 0.07, 1.1, 0.5));
|
|
535
|
+
bleep(L2 + 4.1, 'E4', 0.04, 0.6, 0, true); // CHECK band moves on
|
|
536
|
+
|
|
537
|
+
// ---- P103: accuracy bars step in (one bar per 1.3/12 s from tl 0.85), then 97% flashes (tl 2.15)
|
|
538
|
+
page(P[3]);
|
|
539
|
+
const L3 = P[3];
|
|
540
|
+
for (let i = 1; i < 12; i++) bleep(L3 + 0.85 + (i * 1.3) / 12, 262 * Math.pow(2, (i * 12 / 11) / 12), 0.04, 0.75, -0.5 + i * 0.05, true);
|
|
541
|
+
const BEST = L3 + 2.15 + 1e-3;
|
|
542
|
+
k.play('impact', BEST, { f: 110, gain: 0.8 });
|
|
543
|
+
['C4', 'E4', 'G4', 'C5'].forEach((n, i) => { bleep(BEST + i * 0.06, n, 0.09, 1.3, -0.2); });
|
|
544
|
+
['C5', 'E5', 'G5'].forEach((n) => k.tone(BEST + 0.24, { f: n, type: 'square', dur: 0.8, gain: 0.045, a: 0.003, hold: 0.45, filter: 'lowpass', f2: 2000, verb: 0.2 }));
|
|
545
|
+
k.tone(BEST + 0.24, { f: 'C3', type: 'triangle', dur: 0.9, gain: 0.2, hold: 0.4 });
|
|
546
|
+
bleep(13.0, 'G4', 0.05, 0.5, 0, true); // 97% flashes back on (Flash attribute) just as P104 is being searched
|
|
547
|
+
|
|
548
|
+
// ---- P104: takeaway. BETTER GUESSES lands as the wipe passes row 14 (tl 0.67), staircase completes at tl 0.9
|
|
549
|
+
page(P[4]);
|
|
550
|
+
const L4 = P[4];
|
|
551
|
+
const TAKE = L4 + 0.35 + 7 / 22;
|
|
552
|
+
bleep(TAKE, 'B4', 0.08, 0.8); bleep(TAKE + 0.09, 'C5', 0.5, 0.8); bleep(TAKE + 0.09, 'G4', 0.5, 0.5);
|
|
553
|
+
k.tone(TAKE + 0.09, { f: 'C3', type: 'triangle', dur: 1.2, gain: 0.14, hold: 0.3 });
|
|
554
|
+
// (the staircase of blocks is revealed by the wipe itself, rows 17-22 at tl 0.76-0.85, so the wipe clicks cover it)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
registerStyle({
|
|
558
|
+
id: 'teletext',
|
|
559
|
+
name: 'Teletext Page',
|
|
560
|
+
category: 'Retro UI & Neon',
|
|
561
|
+
description: 'A broadcast teletext page: strict 40x25 character grid, eight saturated colours, 2x3 sextant mosaic graphics with one colour per cell, double-height headlines and Fastext links, seen through a CRT.',
|
|
562
|
+
motion: 'Only the header shows while the page counter rolls in green, then the page lands top-down two rows at a time; mosaic bars step up a sub-block at a time, LATEST and the robot eyes flash, the ticker hops whole characters and the clock ticks.',
|
|
563
|
+
fonts: ['400 33px "VT323"', '900 11.2px "Archivo Black"'],
|
|
564
|
+
frameT: 4.8,
|
|
565
|
+
duration: 5,
|
|
566
|
+
readsPixels: true,
|
|
567
|
+
explainerDuration: 14.8,
|
|
568
|
+
explainerNote: 'Each beat is a new teletext page (P100-P104): the header searches in green while the old page lingers, then the new page overwrites it two rows at a time; arrows, pictures and bar charts are sextant mosaics, numbers count in double-height digits and the key moments use the Flash attribute.',
|
|
569
|
+
explainer(ctx, W, H_, t) {
|
|
570
|
+
let i = 0; for (let k = 0; k < EX_PAGES.length; k++) if (t >= EX_PAGES[k][0]) i = k;
|
|
571
|
+
const tl = t - EX_PAGES[i][0], SEARCH = 0.35;
|
|
572
|
+
const gn = buildEx(i, t), go = i > 0 ? buildEx(i - 1, t) : newGrid();
|
|
573
|
+
let g;
|
|
574
|
+
if (tl < SEARCH) { g = go; exHeader(g, EX_PAGES[i][1], t, true); }
|
|
575
|
+
else {
|
|
576
|
+
const rows = 2 * Math.floor((tl - SEARCH) * 22) + 2;
|
|
577
|
+
g = gn.map((row, r) => (r === 0 || r < rows ? row : go[r]));
|
|
578
|
+
}
|
|
579
|
+
renderTT(ctx, W, H_, g, t);
|
|
580
|
+
},
|
|
581
|
+
explainerSound(k) { ttSound(k); },
|
|
582
|
+
draw(ctx, W, H_, t) {
|
|
583
|
+
const g = buildPage(t);
|
|
584
|
+
const cw = W / COLS, rh = H_ / ROWS;
|
|
585
|
+
const X = (c) => Math.round(c * cw), Y = (r) => Math.round(r * rh);
|
|
586
|
+
ttCache(W, H_);
|
|
587
|
+
const L = scratch('page', W, H_), c = L.getContext('2d');
|
|
588
|
+
c.fillStyle = '#000'; c.fillRect(0, 0, W, H_);
|
|
589
|
+
// reveal rows progressively at the start (page "arriving")
|
|
590
|
+
const rowsShown = t < 0.9 ? 1 : Math.min(ROWS, 2 * Math.floor((t - 0.9) * 22) + 2); // rows land two per field
|
|
591
|
+
for (let r = 0; r < ROWS; r++) {
|
|
592
|
+
if (r >= rowsShown && r !== 0) continue;
|
|
593
|
+
for (let col = 0; col < COLS; col++) {
|
|
594
|
+
const cell = g[r][col];
|
|
595
|
+
const x0 = X(col), x1 = X(col + 1), y0 = Y(r), y1 = Y(r + 1);
|
|
596
|
+
c.fillStyle = PAL[cell.bg]; c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
597
|
+
if (cell.mos && !(cell.flash && Math.floor(t * 1.5) % 3 === 2)) {
|
|
598
|
+
c.fillStyle = PAL[cell.fg];
|
|
599
|
+
for (let j = 0; j < 3; j++) for (let i = 0; i < 2; i++) {
|
|
600
|
+
if (!(cell.mos & (1 << (j * 2 + i)))) continue;
|
|
601
|
+
const sx0 = Math.round(x0 + (i * (x1 - x0)) / 2), sx1 = Math.round(x0 + ((i + 1) * (x1 - x0)) / 2);
|
|
602
|
+
const sy0 = Math.round(r * rh + (j * rh) / 3), sy1 = Math.round(r * rh + ((j + 1) * rh) / 3);
|
|
603
|
+
c.fillRect(sx0, sy0, sx1 - sx0, sy1 - sy0);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// text pass (after backgrounds so double-height glyphs spill into the row below)
|
|
609
|
+
for (let r = 0; r < ROWS; r++) {
|
|
610
|
+
if (r >= rowsShown && r !== 0) continue;
|
|
611
|
+
for (let col = 0; col < COLS; col++) {
|
|
612
|
+
const cell = g[r][col];
|
|
613
|
+
if (cell.mos || cell.ch === ' ' || cell.dh === 2) continue;
|
|
614
|
+
drawGlyph(c, cell, X(col), Y(r), cw, rh); // cached rounded glyph (hero cells never use double width)
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ---- CRT presentation: soft focus, gun misconvergence, shadow mask, bloom, scanlines, curved glass ----
|
|
619
|
+
crtPresent(ctx, W, H_, L, t);
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
}
|