@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": 44,
|
|
3
|
+
"name": "Felt & Patchwork",
|
|
4
|
+
"nameZh": "毛毡与拼布",
|
|
5
|
+
"family": "Paper & Craft",
|
|
6
|
+
"familyZh": "纸艺与手工",
|
|
7
|
+
"description": "A cosy patchwork quilt of cotton prints with puffy quilted squares, topped with fuzzy felt appliqué edged in blanket stitch, button \"nodes\" sewn on and joined by running-stitch thread, plus an embroidered label.",
|
|
8
|
+
"motionNotes": "Motion: Thread draws itself stitch by stitch between the buttons, felt pieces drop on with a soft squash, and the quilt breathes with a slow puff of the padding.",
|
|
9
|
+
"explainerNotes": "Explainer: The camera pans along one long patchwork quilt; felt pieces drop on with a lifted shadow and soft squash, arrows and emphasis rings are thread sewn on stitch by stitch, labels are woven tags stitched onto what they name, the counter is cut felt digits swapped as it climbs, and the model's weights are buttons whose thread-X turns when it adjusts.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "felt-quilt",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
{
|
|
2
|
+
// ---------- Green-phosphor CRT terminal: text -> scanlines -> barrel remap w/ chromatic fringe -> bloom -> glass ----------
|
|
3
|
+
const LINES = [
|
|
4
|
+
['NEURAL SYSTEMS CORP. NX-80 TERMINAL BIOS 2.04', 0],
|
|
5
|
+
['(C) 1983 ALL RIGHTS RESERVED', 0],
|
|
6
|
+
['', 0],
|
|
7
|
+
['MEMORY CHECK ............ 65536K OK', 1],
|
|
8
|
+
['SYNAPSE BUS ............. ONLINE', 1],
|
|
9
|
+
['LOADING WEIGHTS [BAR]', 2],
|
|
10
|
+
[' layer 096/117 attn.heads=32 ok', 1],
|
|
11
|
+
[' layer 097/117 attn.heads=32 ok', 1],
|
|
12
|
+
['CALIBRATING CORTEX ...... DONE', 1],
|
|
13
|
+
['', 0],
|
|
14
|
+
['>> RUN CONSCIOUSNESS.EXE', 3],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
let BEZEL = null; // static plastic housing, built once per size
|
|
18
|
+
// shared tube pipeline for the hero and the explainer (bezel / remap / glass)
|
|
19
|
+
function crtCompose(ctx, W, H_, t, paint, pOnT, boost) {
|
|
20
|
+
const r = H.rng(80), nz = H.makeNoise(5);
|
|
21
|
+
// tube geometry
|
|
22
|
+
const TX = 96, TY = 46, TW = 1088, TH = 576;
|
|
23
|
+
|
|
24
|
+
// ---------- 1. BEZEL (plastic housing), static: built once per size ----------
|
|
25
|
+
const rrOn = (c, x, y, w, h, rad) => { c.beginPath(); c.moveTo(x + rad, y); c.arcTo(x + w, y, x + w, y + h, rad); c.arcTo(x + w, y + h, x, y + h, rad); c.arcTo(x, y + h, x, y, rad); c.arcTo(x, y, x + w, y, rad); c.closePath(); };
|
|
26
|
+
const rr = (x, y, w, h, rad) => rrOn(ctx, x, y, w, h, rad);
|
|
27
|
+
if (!BEZEL || BEZEL.width !== W || BEZEL.height !== H_) {
|
|
28
|
+
BEZEL = H.layer(W, H_);
|
|
29
|
+
const c = BEZEL.getContext('2d');
|
|
30
|
+
const bz = c.createLinearGradient(0, 0, 0, H_);
|
|
31
|
+
bz.addColorStop(0, '#3b3934'); bz.addColorStop(0.5, '#2c2a26'); bz.addColorStop(1, '#1d1c19');
|
|
32
|
+
c.fillStyle = bz; c.fillRect(0, 0, W, H_);
|
|
33
|
+
// moulded ABS texture: per-pixel fine grain + soft mottling (+-3%)
|
|
34
|
+
const img = c.getImageData(0, 0, W, H_), d = img.data, pn = H.makeNoise(31), gr = H.rng(311);
|
|
35
|
+
for (let y = 0; y < H_; y++) for (let x = 0; x < W; x++) {
|
|
36
|
+
const k = 1 + (gr() - 0.5) * 0.06 + pn.n2(x * 0.9, y * 0.9) * 0.025 + pn.n2(x * 0.012, y * 0.012) * 0.03, i = (y * W + x) * 4;
|
|
37
|
+
d[i] *= k; d[i + 1] *= k; d[i + 2] *= k;
|
|
38
|
+
}
|
|
39
|
+
c.putImageData(img, 0, 0);
|
|
40
|
+
// key light from the upper left, falling off toward the lower right
|
|
41
|
+
const kl = c.createRadialGradient(W * 0.12, -H_ * 0.1, 40, W * 0.3, H_ * 0.2, W * 0.95);
|
|
42
|
+
kl.addColorStop(0, 'rgba(255,238,205,0.13)'); kl.addColorStop(0.45, 'rgba(255,238,205,0.04)'); kl.addColorStop(1, 'rgba(0,0,0,0)');
|
|
43
|
+
c.fillStyle = kl; c.fillRect(0, 0, W, H_);
|
|
44
|
+
const fo = c.createLinearGradient(W * 0.35, H_ * 0.3, W, H_);
|
|
45
|
+
fo.addColorStop(0, 'rgba(0,0,0,0)'); fo.addColorStop(1, 'rgba(0,0,0,0.28)');
|
|
46
|
+
c.fillStyle = fo; c.fillRect(0, 0, W, H_);
|
|
47
|
+
// outer edge highlight (rounded housing edge: lit top/left, shadowed bottom/right)
|
|
48
|
+
c.strokeStyle = 'rgba(255,240,210,0.08)'; c.lineWidth = 2; c.strokeRect(6, 6, W - 12, H_ - 12);
|
|
49
|
+
c.strokeStyle = 'rgba(255,240,210,0.1)'; c.lineWidth = 1.5; c.beginPath(); c.moveTo(7, H_ - 40); c.lineTo(7, 7); c.lineTo(W - 40, 7); c.stroke();
|
|
50
|
+
// recessed opening around the tube (bevel: dark top-left, lit bottom edge)
|
|
51
|
+
const bev = c.createLinearGradient(0, TY - 30, 0, TY + TH + 30);
|
|
52
|
+
bev.addColorStop(0, '#0e0d0b'); bev.addColorStop(0.85, '#1a1916'); bev.addColorStop(1, '#4a463e');
|
|
53
|
+
c.save(); c.shadowColor = 'rgba(255,236,200,0.16)'; c.shadowOffsetY = 2; c.shadowBlur = 3;
|
|
54
|
+
rrOn(c, TX - 30, TY - 28, TW + 60, TH + 58, 38); c.fillStyle = bev; c.fill(); c.restore();
|
|
55
|
+
// moulded highlight on the lower lip of the recess, and a soft shadow line under the top lip
|
|
56
|
+
c.save(); rrOn(c, TX - 30, TY - 28, TW + 60, TH + 58, 38); c.clip();
|
|
57
|
+
const lip = c.createLinearGradient(0, TY + TH + 18, 0, TY + TH + 30);
|
|
58
|
+
lip.addColorStop(0, 'rgba(255,240,210,0)'); lip.addColorStop(0.7, 'rgba(255,240,210,0.16)'); lip.addColorStop(1, 'rgba(255,240,210,0.04)');
|
|
59
|
+
c.fillStyle = lip; c.fillRect(TX - 30, TY + TH + 16, TW + 60, 16);
|
|
60
|
+
c.strokeStyle = 'rgba(0,0,0,0.5)'; c.lineWidth = 3; rrOn(c, TX - 29, TY - 27, TW + 58, TH + 56, 37); c.stroke();
|
|
61
|
+
c.restore();
|
|
62
|
+
c.strokeStyle = 'rgba(255,240,215,0.22)'; c.lineWidth = 1;
|
|
63
|
+
c.beginPath(); c.moveTo(TX + 10, TY + TH + 29.5); c.lineTo(TX + TW - 10, TY + TH + 29.5); c.stroke();
|
|
64
|
+
rrOn(c, TX - 14, TY - 12, TW + 28, TH + 26, 30); c.fillStyle = '#050605'; c.fill();
|
|
65
|
+
}
|
|
66
|
+
ctx.drawImage(BEZEL, 0, 0);
|
|
67
|
+
|
|
68
|
+
const src = H.layer(TW, TH), s = src.getContext('2d', { willReadFrequently: true });
|
|
69
|
+
s.fillStyle = '#000'; s.fillRect(0, 0, TW, TH);
|
|
70
|
+
s.fillStyle = '#fff'; s.strokeStyle = '#fff'; s.textBaseline = 'top';
|
|
71
|
+
paint(s, TW, TH);
|
|
72
|
+
s.globalAlpha = 1; s.globalCompositeOperation = 'source-over'; s.shadowBlur = 0;
|
|
73
|
+
// ---------- 3. bloom / halation layer from the source ----------
|
|
74
|
+
const small = H.layer(TW / 4, TH / 4), sm = small.getContext('2d');
|
|
75
|
+
sm.filter = 'blur(3px)'; sm.drawImage(src, 0, 0, TW / 4, TH / 4);
|
|
76
|
+
const bloom = H.layer(TW, TH), bl = bloom.getContext('2d', { willReadFrequently: true });
|
|
77
|
+
bl.imageSmoothingEnabled = true; bl.drawImage(small, 0, 0, TW, TH);
|
|
78
|
+
const wide = H.layer(TW / 8, TH / 8), wd = wide.getContext('2d');
|
|
79
|
+
wd.filter = 'blur(4px)'; wd.drawImage(src, 0, 0, TW / 8, TH / 8);
|
|
80
|
+
bl.globalCompositeOperation = 'lighter'; bl.globalAlpha = 0.9; bl.drawImage(wide, 0, 0, TW, TH);
|
|
81
|
+
const S = s.getImageData(0, 0, TW, TH).data, Bl = bl.getImageData(0, 0, TW, TH).data;
|
|
82
|
+
|
|
83
|
+
// ---------- 4. per-pixel CRT remap ----------
|
|
84
|
+
const out = ctx.getImageData(0, 0, W, H_), O = out.data;
|
|
85
|
+
const K = 0.065, CA = 0.0016; // barrel strength, chromatic offset
|
|
86
|
+
const humY = ((t * 0.18) % 1.3 - 0.15) * TH;
|
|
87
|
+
const flick = 1 + 0.025 * Math.sin(t * 60 * 2 * Math.PI * 0.5);
|
|
88
|
+
// power-on: dot -> horizontal line -> raster snaps open with a flash, then phosphor warm-up
|
|
89
|
+
const pOn = pOnT;
|
|
90
|
+
const wBand = pOn < 0 ? -1 : H.clamp(pOn / 0.1), hBand = pOn < 0.1 ? 0.007 : 0.007 + H.ease.out(H.clamp((pOn - 0.1) / 0.28));
|
|
91
|
+
const flash = pOn < 0.1 ? 1.5 : Math.max(0, 1.3 * (1 - (pOn - 0.1) / 0.55));
|
|
92
|
+
const warm = H.clamp(0.45 + (pOn - 0.3) * 0.9, 0.45, 1);
|
|
93
|
+
const sample = (A, x, y, ch) => { // bilinear
|
|
94
|
+
if (x < 0 || y < 0 || x >= TW - 1 || y >= TH - 1) return 0;
|
|
95
|
+
const x0 = x | 0, y0 = y | 0, fx = x - x0, fy = y - y0, i = (y0 * TW + x0) * 4 + ch;
|
|
96
|
+
return (A[i] * (1 - fx) + A[i + 4] * fx) * (1 - fy) + (A[i + TW * 4] * (1 - fx) + A[i + TW * 4 + 4] * fx) * fy;
|
|
97
|
+
};
|
|
98
|
+
const rad = 34; // tube corner radius (output space)
|
|
99
|
+
for (let y = TY - 12; y < TY + TH + 14; y++) for (let x = TX - 14; x < TX + TW + 14; x++) {
|
|
100
|
+
// rounded-rect tube mask with soft edge
|
|
101
|
+
const qx = Math.max(Math.abs(x - (TX + TW / 2)) - (TW / 2 + 8 - rad), 0), qy = Math.max(Math.abs(y - (TY + TH / 2)) - (TH / 2 + 8 - rad), 0);
|
|
102
|
+
const dEdge = rad - Math.hypot(qx, qy);
|
|
103
|
+
if (dEdge < 0) continue;
|
|
104
|
+
const u = ((x - TX) / TW) * 2 - 1, v = ((y - TY) / TH) * 2 - 1;
|
|
105
|
+
const r2 = u * u * 1.0 + v * v * 1.0;
|
|
106
|
+
let col = [0, 0, 0];
|
|
107
|
+
let lumG = 0;
|
|
108
|
+
for (let c = 0; c < 3; c++) {
|
|
109
|
+
const f = 1 + K * r2 + (c - 1) * CA * r2 * 2.2; // R further out, B further in
|
|
110
|
+
const su = u * f * 0.985, sv = v * f * 0.99;
|
|
111
|
+
const sx = (su + 1) * 0.5 * TW, sy = (sv + 1) * 0.5 * TH;
|
|
112
|
+
// scanlines in SOURCE space so they bow with the glass
|
|
113
|
+
const scan = 0.42 + 0.58 * Math.pow(Math.cos((sy / 3) * Math.PI), 2);
|
|
114
|
+
const base = sample(S, sx, sy, 1) / 255;
|
|
115
|
+
const glow = sample(Bl, sx, sy, 1) / 255;
|
|
116
|
+
const hum = 1 + 0.07 * Math.exp(-(((sy - humY) / 70) ** 2));
|
|
117
|
+
col[c] = (base * scan + glow * 0.85) * hum * flick * warm * boost;
|
|
118
|
+
if (c === 1) lumG = col[c];
|
|
119
|
+
}
|
|
120
|
+
if (pOn < 0.7) {
|
|
121
|
+
const bandA = wBand < 0 ? 0 : H.clamp((hBand - Math.abs(v)) * 90) * H.clamp((wBand - Math.abs(u)) * 40);
|
|
122
|
+
const fe = flash * (1 - 0.35 * Math.abs(u));
|
|
123
|
+
for (let c = 0; c < 3; c++) col[c] = (col[c] + fe) * bandA;
|
|
124
|
+
}
|
|
125
|
+
// phosphor tint: P1 green; hot cores go toward white-green
|
|
126
|
+
const vig = Math.pow(H.clamp(1 - r2 * 0.28), 1.4);
|
|
127
|
+
const n = (r() - 0.5) * 0.035;
|
|
128
|
+
const R = col[0], G = col[1], B = col[2];
|
|
129
|
+
const hotR = Math.max(0, R - 0.8) * 0.8, hotB = Math.max(0, B - 0.8) * 0.7;
|
|
130
|
+
let or = 7 + (0.2 * Math.pow(R, 1.6) + hotR * 0.3) * 255, og = 13 + Math.min(1.15, G) * 235, ob = 9 + (0.34 * Math.pow(B, 1.6) + hotB * 0.28) * 255;
|
|
131
|
+
or = (or * vig + n * 255); og = (og * vig + n * 255); ob = (ob * vig + n * 255);
|
|
132
|
+
// edge falloff into the dark bezel lip
|
|
133
|
+
const a = H.clamp(dEdge / 6);
|
|
134
|
+
const i = (y * W + x) * 4;
|
|
135
|
+
O[i] = O[i] * (1 - a) + or * a; O[i + 1] = O[i + 1] * (1 - a) + og * a; O[i + 2] = O[i + 2] * (1 - a) + ob * a;
|
|
136
|
+
}
|
|
137
|
+
ctx.putImageData(out, 0, 0);
|
|
138
|
+
|
|
139
|
+
// ---------- 5. glass: curvature shading + room reflections ----------
|
|
140
|
+
ctx.save();
|
|
141
|
+
rr(TX - 6, TY - 4, TW + 12, TH + 10, rad); ctx.clip();
|
|
142
|
+
// inner edge darkening (glass curving away)
|
|
143
|
+
const ed = ctx.createRadialGradient(TX + TW / 2, TY + TH / 2, TH * 0.45, TX + TW / 2, TY + TH / 2, TW * 0.62);
|
|
144
|
+
ed.addColorStop(0, 'rgba(0,0,0,0)'); ed.addColorStop(1, 'rgba(0,0,0,0.55)');
|
|
145
|
+
ctx.fillStyle = ed; ctx.fillRect(TX - 10, TY - 10, TW + 20, TH + 20);
|
|
146
|
+
ctx.globalCompositeOperation = 'screen';
|
|
147
|
+
// broad soft sheen top-left
|
|
148
|
+
const sh = ctx.createRadialGradient(TX + TW * 0.22, TY + TH * 0.12, 10, TX + TW * 0.22, TY + TH * 0.12, TW * 0.85);
|
|
149
|
+
sh.addColorStop(0, 'rgba(200,220,210,0.07)'); sh.addColorStop(0.5, 'rgba(200,220,210,0.025)'); sh.addColorStop(1, 'rgba(200,220,210,0)');
|
|
150
|
+
ctx.fillStyle = sh; ctx.fillRect(TX, TY, TW, TH);
|
|
151
|
+
// window reflection (soft trapezoid) on the right
|
|
152
|
+
ctx.filter = 'blur(36px)';
|
|
153
|
+
const wr = ctx.createLinearGradient(TX + TW * 0.69, 0, TX + TW * 0.9, 0);
|
|
154
|
+
wr.addColorStop(0, 'rgba(190,210,220,0)'); wr.addColorStop(0.5, 'rgba(190,210,220,0.04)'); wr.addColorStop(1, 'rgba(190,210,220,0)');
|
|
155
|
+
ctx.fillStyle = wr;
|
|
156
|
+
ctx.beginPath(); ctx.moveTo(TX + TW * 0.7, TY + 20); ctx.lineTo(TX + TW * 0.9, TY + 30); ctx.lineTo(TX + TW * 0.88, TY + TH * 0.42); ctx.lineTo(TX + TW * 0.69, TY + TH * 0.38); ctx.closePath(); ctx.fill();
|
|
157
|
+
|
|
158
|
+
// thin specular arc along the top edge of the glass
|
|
159
|
+
ctx.filter = 'blur(2px)';
|
|
160
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.12)'; ctx.lineWidth = 3;
|
|
161
|
+
ctx.beginPath(); ctx.moveTo(TX + 60, TY + 14); ctx.quadraticCurveTo(TX + TW / 2, TY + 2, TX + TW - 60, TY + 14); ctx.stroke();
|
|
162
|
+
ctx.filter = 'none';
|
|
163
|
+
ctx.restore();
|
|
164
|
+
|
|
165
|
+
// ---------- 6. bezel details: badge + power LED ----------
|
|
166
|
+
ctx.save();
|
|
167
|
+
ctx.font = '700 15px "Space Mono"'; ctx.textBaseline = 'middle';
|
|
168
|
+
const by = TY + TH + 44;
|
|
169
|
+
ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillText('NX-80 NEURAL TERMINAL', TX + 1, by + 1);
|
|
170
|
+
ctx.fillStyle = '#8d877a'; ctx.fillText('NX-80 NEURAL TERMINAL', TX, by);
|
|
171
|
+
ctx.font = '400 11px "Space Mono"'; ctx.fillStyle = '#5f5a50'; ctx.fillText('HIGH RESOLUTION P1 PHOSPHOR', TX + 270, by + 1);
|
|
172
|
+
// LED
|
|
173
|
+
const lx = TX + TW - 30;
|
|
174
|
+
const lg = ctx.createRadialGradient(lx, by, 0, lx, by, 22); lg.addColorStop(0, 'rgba(90,255,120,0.55)'); lg.addColorStop(1, 'rgba(90,255,120,0)');
|
|
175
|
+
ctx.fillStyle = lg; ctx.fillRect(lx - 22, by - 22, 44, 44);
|
|
176
|
+
ctx.fillStyle = '#b8ffc4'; ctx.beginPath(); ctx.arc(lx, by, 4, 0, 7); ctx.fill();
|
|
177
|
+
ctx.fillStyle = '#5f5a50'; ctx.font = '400 11px "Space Mono"'; ctx.textAlign = 'right'; ctx.fillText('POWER', lx - 16, by + 1);
|
|
178
|
+
// two knobs
|
|
179
|
+
[lx - 120, lx - 170].forEach((kx) => {
|
|
180
|
+
const kg = ctx.createRadialGradient(kx - 3, by - 4, 1, kx, by, 12); kg.addColorStop(0, '#5a564e'); kg.addColorStop(1, '#151412');
|
|
181
|
+
ctx.fillStyle = kg; ctx.beginPath(); ctx.arc(kx, by, 11, 0, 7); ctx.fill();
|
|
182
|
+
ctx.strokeStyle = 'rgba(0,0,0,0.6)'; ctx.lineWidth = 1; ctx.stroke();
|
|
183
|
+
ctx.strokeStyle = '#8d877a'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(kx, by); ctx.lineTo(kx + 6, by - 7); ctx.stroke();
|
|
184
|
+
});
|
|
185
|
+
ctx.restore();
|
|
186
|
+
// green spill from the screen onto the bezel lip
|
|
187
|
+
ctx.save(); ctx.globalCompositeOperation = 'screen';
|
|
188
|
+
const spill = ctx.createLinearGradient(0, TY + TH, 0, TY + TH + 30); spill.addColorStop(0, 'rgba(40,140,60,0.18)'); spill.addColorStop(1, 'rgba(40,140,60,0)');
|
|
189
|
+
ctx.fillStyle = spill; ctx.fillRect(TX, TY + TH + 4, TW, 30);
|
|
190
|
+
ctx.restore();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ================= EXPLAINER: a terminal session explains "How does AI learn?" =================
|
|
194
|
+
const ASCII_CAT = [' /\\_/\\ ', ' ( o.o ) ', ' > ^ < '];
|
|
195
|
+
const ASCII_DOG = [' / \\__ ', ' ( @\\_', ' / ___O'];
|
|
196
|
+
function crtExplainer(ctx, W, H_, t) {
|
|
197
|
+
const fmt = (n) => String(Math.floor(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
198
|
+
const ease = (a, d) => H.ease.inOut(H.clamp((t - a) / d));
|
|
199
|
+
const typed = (str, a, cps = 28) => str.slice(0, Math.max(0, Math.floor((t - a) * cps)));
|
|
200
|
+
const blinkOn = Math.floor(t * 2.2) % 2 === 0;
|
|
201
|
+
let boost = 1;
|
|
202
|
+
if (t > 7.55 && t < 7.75) boost = 1.45; // phosphor flash on the wrong guess
|
|
203
|
+
if (t > 12.45 && t < 12.6) boost = 1.3; // and on 97%
|
|
204
|
+
|
|
205
|
+
const paint = (s, TW, TH) => {
|
|
206
|
+
s.font = '26px "VT323"';
|
|
207
|
+
const cw = s.measureText('M').width, LH = 28;
|
|
208
|
+
const txt = (str, x, y, font = '26px "VT323"', a = 1) => { s.font = font; s.globalAlpha = a; s.shadowColor = '#fff'; s.shadowBlur = 3; s.fillText(str, x, y); s.shadowBlur = 0; s.globalAlpha = 1; return s.measureText(str).width; };
|
|
209
|
+
const inv = (str, x, y, font = '26px "VT323"') => { s.font = font; const w = s.measureText(str).width, h = parseInt(font) || 26; s.globalAlpha = 0.8; s.fillRect(x - 4, y, w + 8, h); s.globalAlpha = 1; s.globalCompositeOperation = 'destination-out'; s.fillText(str, x, y); s.lineWidth = Math.max(1, h / 30); s.strokeText(str, x, y); s.globalCompositeOperation = 'source-over'; return w; };
|
|
210
|
+
const cursor = (x, y, h = 22) => { if (blinkOn) s.fillRect(x + 2, y + 3, h * 0.55, h); };
|
|
211
|
+
const line = (pts, w = 2, a = 1) => { s.globalAlpha = a; s.lineWidth = w; s.beginPath(); pts.forEach(([x, y], i) => (i ? s.lineTo(x, y) : s.moveTo(x, y))); s.stroke(); s.globalAlpha = 1; };
|
|
212
|
+
const partial = (pts, f) => { // cut a polyline to fraction f of its length
|
|
213
|
+
if (f >= 1) return pts; let L = 0; for (let i = 1; i < pts.length; i++) L += Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
|
|
214
|
+
let left = L * Math.max(0, f); const out = [pts[0]];
|
|
215
|
+
for (let i = 1; i < pts.length; i++) { const d = Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]); if (d >= left) { const k = d ? left / d : 0; out.push([pts[i - 1][0] + (pts[i][0] - pts[i - 1][0]) * k, pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * k]); break; } out.push(pts[i]); left -= d; }
|
|
216
|
+
return out;
|
|
217
|
+
};
|
|
218
|
+
const arrowTo = (pts, f, w = 2) => { const p = partial(pts, f); line(p, w); if (f > 0.05 && p.length > 1) { const [x2, y2] = p[p.length - 1], [x1, y1] = p[p.length - 2]; const a = Math.atan2(y2 - y1, x2 - x1); line([[x2 + Math.cos(a + 2.5) * 13, y2 + Math.sin(a + 2.5) * 13], [x2, y2], [x2 + Math.cos(a - 2.5) * 13, y2 + Math.sin(a - 2.5) * 13]], w); } };
|
|
219
|
+
const boxR = (x, y, w, h, f = 1, lw = 2) => line(partial([[x, y], [x + w, y], [x + w, y + h], [x, y + h], [x, y]], f), lw);
|
|
220
|
+
const asciiPic = (rows, x, y) => rows.forEach((rw, i) => txt(rw, x, y + i * LH));
|
|
221
|
+
|
|
222
|
+
// ---------- scene drawers (each in its own coordinates; the V-hold roll moves between them) ----------
|
|
223
|
+
const scene1and2 = () => {
|
|
224
|
+
const scroll = ease(2.05, 0.5) * 300;
|
|
225
|
+
s.save(); s.translate(0, -scroll);
|
|
226
|
+
// BEAT 1: the user asks the question; the machine echoes it big
|
|
227
|
+
const q = '> how does ai learn?';
|
|
228
|
+
const tq = typed(q, 0.1, 32);
|
|
229
|
+
const qw = txt(tq, 40, 40, '52px "VT323"'); if (tq.length < q.length || t < 0.75) { if (blinkOn) s.fillRect(46 + qw, 46, 24, 42); }
|
|
230
|
+
if (t > 0.75) {
|
|
231
|
+
const big = 'HOW DOES AI LEARN?', bt = typed(big, 0.75, 30);
|
|
232
|
+
txt(bt, 40, 150, '124px "VT323"');
|
|
233
|
+
if (t > 1.4) txt('='.repeat(44), 40, 282, '32px "VT323"', 0.8);
|
|
234
|
+
}
|
|
235
|
+
// BEAT 2 (300px lower, scrolls into view): the training data stream
|
|
236
|
+
s.translate(0, 300);
|
|
237
|
+
const cmd = '$ train --data ./examples --labels';
|
|
238
|
+
if (t > 2.1) txt(typed(cmd, 2.1, 70), 40, 30);
|
|
239
|
+
if (t > 2.2) {
|
|
240
|
+
const F32 = '32px "VT323"';
|
|
241
|
+
// log of labelled examples, scrolling fast (labels are the story; filenames are texture)
|
|
242
|
+
const n = Math.floor((t - 2.2) / 0.085);
|
|
243
|
+
for (let k = 0; k < 7; k++) {
|
|
244
|
+
const i = n - 6 + k; if (i < 0) continue;
|
|
245
|
+
const isCat = (i * 7) % 3 !== 0, y = 70 + k * 36;
|
|
246
|
+
const id = String(4800 + i * 37).padStart(7, '0');
|
|
247
|
+
const w = inv(isCat ? 'cat' : 'dog', 44, y, F32);
|
|
248
|
+
txt('img_' + id, 44 + w + 16, y, F32, k === 6 ? 1 : 0.45 + k * 0.07);
|
|
249
|
+
}
|
|
250
|
+
// the current example as an ASCII picture with its label landing under it
|
|
251
|
+
const cur = Math.floor((t - 2.2) / 0.55), isCat = cur % 3 !== 2;
|
|
252
|
+
boxR(410, 70, 250, 150, ease(2.2, 0.3));
|
|
253
|
+
if (t > 2.4) { s.font = '38px "VT323"'; (isCat ? ASCII_CAT : ASCII_DOG).forEach((rw, i) => txt(rw, 428, 86 + i * 38, '38px "VT323"')); inv(isCat ? ' cat ' : ' dog ', 500, 226, '38px "VT323"'); }
|
|
254
|
+
// marching arrow into the model
|
|
255
|
+
if (t > 2.6) {
|
|
256
|
+
const f = ease(2.6, 0.4);
|
|
257
|
+
arrowTo([[670, 145], [790, 145]], f, 3);
|
|
258
|
+
const m = Math.floor(t * 12) % 4;
|
|
259
|
+
for (let k = 0; k < 4; k++) if (k !== m && f >= 1) txt('>', 690 + k * 22, 106, F32, 0.7);
|
|
260
|
+
}
|
|
261
|
+
// the model box
|
|
262
|
+
if (t > 2.5) {
|
|
263
|
+
boxR(800, 64, 250, 186, ease(2.5, 0.4), 3);
|
|
264
|
+
if (t > 2.8) {
|
|
265
|
+
inv(' MODEL ', 868, 50, '38px "VT323"');
|
|
266
|
+
['o o o', ' \\ / \\ / ', ' o o ', ' \\ / ', ' o '].forEach((rw, i) => txt(rw, 838, 98 + i * 30, F32));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// counter: the hero number of this beat
|
|
270
|
+
if (t > 2.8) {
|
|
271
|
+
txt('EXAMPLES SEEN', 410, 262, '38px "VT323"');
|
|
272
|
+
const v = 1e6 * Math.pow(H.clamp((t - 2.9) / 2.8), 2);
|
|
273
|
+
txt(fmt(v), 410, 296, '110px "VT323"');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
s.restore();
|
|
277
|
+
};
|
|
278
|
+
const scene3 = () => {
|
|
279
|
+
s.save(); s.translate(0, 34); s.scale(1.06, 1.06);
|
|
280
|
+
txt('$ step --verbose', 40, 24, '30px "VT323"', 0.7);
|
|
281
|
+
// loop indicator with the active phase in inverse video
|
|
282
|
+
const phase = t < 7.5 ? 0 : t < 8.35 ? 1 : t < 9.4 ? 2 : 0;
|
|
283
|
+
const names = ['GUESS', 'CHECK', 'ADJUST'];
|
|
284
|
+
let x = 40;
|
|
285
|
+
names.forEach((nm, i) => { const w = i === phase ? inv(' ' + nm + ' ', x, 68, '38px "VT323"') : txt(' ' + nm + ' ', x, 68, '38px "VT323"', 0.6); x += w + 14; if (i < 2) { x += txt('->', x, 68, '38px "VT323"', 0.6) + 14; } });
|
|
286
|
+
txt('<- repeat', x + 6, 68, '38px "VT323"', 0.6);
|
|
287
|
+
// picture + model boxes
|
|
288
|
+
boxR(40, 140, 210, 130); ASCII_CAT.forEach((rw, i) => txt(rw, 54, 150 + i * 36, '38px "VT323"')); txt('new picture', 50, 278, '32px "VT323"', 0.8);
|
|
289
|
+
boxR(470, 130, 250, 200); inv(' MODEL ', 552, 118);
|
|
290
|
+
// weights inside the model (digits roll while adjusting)
|
|
291
|
+
const W0 = [0.42, -1.3, 0.77], W1 = [0.57, -0.86, 0.31];
|
|
292
|
+
const adj = H.clamp((t - 8.55) / 0.75);
|
|
293
|
+
W0.forEach((w0, i) => {
|
|
294
|
+
let v = H.lerp(w0, W1[i], adj);
|
|
295
|
+
if (adj > 0 && adj < 1) v += (H.rng(Math.floor(t * 20) + i)() - 0.5) * 0.4;
|
|
296
|
+
const str = 'w' + (i + 1) + ' = ' + (v >= 0 ? ' ' : '') + v.toFixed(2);
|
|
297
|
+
txt(str, 492, 160 + i * 44, '40px "VT323"', adj > 0 && adj < 1 ? 1 : 0.85);
|
|
298
|
+
});
|
|
299
|
+
// GUESS arrow
|
|
300
|
+
arrowTo([[260, 200], [460, 200]], ease(6.7, 0.4)); if (t > 6.8) txt('guess', 312, 160, '34px "VT323"', 0.9);
|
|
301
|
+
const out1 = t > 7.05 && t < 9.4, out2 = t > 9.7;
|
|
302
|
+
if (t > 6.95) arrowTo([[730, 200], [800, 200]], ease(6.95, 0.2));
|
|
303
|
+
if (out1) {
|
|
304
|
+
txt('dog?', 815, 172, '64px "VT323"');
|
|
305
|
+
if (t > 7.5) { // CHECK: wrong
|
|
306
|
+
line([[810, 172], [930, 236]], 4); line([[930, 172], [810, 236]], 4);
|
|
307
|
+
if (t > 8.2 || Math.floor((t - 7.5) * 6) % 2 === 0) inv(" WRONG: IT'S A CAT ", 680, 262, '38px "VT323"');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ADJUST: error travels back underneath into the model
|
|
311
|
+
if (t > 8.35) {
|
|
312
|
+
const path = [[870, 300], [870, 380], [600, 380], [600, 340]];
|
|
313
|
+
arrowTo(path, ease(8.35, 0.55));
|
|
314
|
+
if (t > 8.5) txt('error', 690, 388, '34px "VT323"');
|
|
315
|
+
}
|
|
316
|
+
if (out2) {
|
|
317
|
+
txt('cat', 815, 172, '64px "VT323"');
|
|
318
|
+
if (t > 9.95) { line([[905, 205], [920, 222], [950, 178]], 4); txt('[ OK ]', 815, 262, '38px "VT323"'); }
|
|
319
|
+
}
|
|
320
|
+
txt('round ' + String(1 + Math.floor(Math.max(0, t - 6.4) * 2.2)).padStart(3, '0'), 40, 430, '32px "VT323"', 0.7);
|
|
321
|
+
s.restore();
|
|
322
|
+
};
|
|
323
|
+
const scene4 = () => {
|
|
324
|
+
txt('$ plot accuracy --rounds 12', 40, 30);
|
|
325
|
+
const ox = 90, oy = 400, ah = 290, bw = 40;
|
|
326
|
+
line([[ox, oy - ah - 10], [ox, oy], [ox + 12 * (bw + 12) + 10, oy]], 2);
|
|
327
|
+
txt('100%', 26, oy - ah - 14, '28px "VT323"', 0.8); txt(' 50%', 26, oy - ah / 2 - 14, '28px "VT323"', 0.8);
|
|
328
|
+
txt('training rounds ->', ox + 260, oy + 8, '32px "VT323"', 0.8);
|
|
329
|
+
const acc = (i) => 97 - 36 * Math.exp(-i / 3.2);
|
|
330
|
+
const nb = H.clamp((t - 11.0) / 1.4) * 12;
|
|
331
|
+
for (let i = 0; i < 12; i++) {
|
|
332
|
+
const g = H.clamp(nb - i); if (g <= 0) break;
|
|
333
|
+
const h = ((acc(i) - 0) / 100) * ah * H.ease.out(g);
|
|
334
|
+
s.globalAlpha = 0.7; for (let y = oy - 4; y > oy - h; y -= 8) s.fillRect(ox + 12 + i * (bw + 12), y - 5, bw, 6); s.globalAlpha = 1; // block characters, stacked
|
|
335
|
+
}
|
|
336
|
+
const val = Math.round(61 + 36 * H.ease.out(H.clamp((t - 11.0) / 1.4)));
|
|
337
|
+
txt('ACCURACY', 760, 100, '40px "VT323"');
|
|
338
|
+
const vs = val + '%';
|
|
339
|
+
if (t > 12.45 && (t > 12.95 || Math.floor((t - 12.45) * 8) % 2 === 0)) { inv(vs, 760, 140, '120px "VT323"'); }
|
|
340
|
+
else txt(vs, 760, 140, '120px "VT323"');
|
|
341
|
+
txt('61% -> ' + val + '%', 764, 262, '38px "VT323"', 0.85);
|
|
342
|
+
if (t > 12.5) { arrowTo([[750, 210], [ox + 11 * (bw + 12) + 40, oy - ((acc(11)) / 100) * ah + 10]], ease(12.5, 0.3)); }
|
|
343
|
+
if (t > 13.0) {
|
|
344
|
+
const tk = '> MORE EXAMPLES -> BETTER GUESSES', ts = typed(tk, 13.0, 55);
|
|
345
|
+
txt(ts, 40, 458, '56px "VT323"'); if (ts.length < tk.length || blinkOn) s.fillRect(40 + s.measureText(ts).width + 6, 464, 26, 44);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
// ---------- V-hold roll transitions between scenes ----------
|
|
349
|
+
const roll = (a, d, from, to) => {
|
|
350
|
+
const k = H.ease.inOut(H.clamp((t - a) / d)), off = k * (TH + 60);
|
|
351
|
+
s.save(); s.translate(0, -off); from(); s.restore();
|
|
352
|
+
s.save(); s.translate(0, TH + 60 - off); to(); s.restore();
|
|
353
|
+
s.fillStyle = '#000'; s.fillRect(0, TH - off - 4, TW, 64); s.fillStyle = '#fff'; // blanking bar
|
|
354
|
+
};
|
|
355
|
+
if (t < 6.1) scene1and2();
|
|
356
|
+
else if (t < 6.55) roll(6.1, 0.45, scene1and2, scene3);
|
|
357
|
+
else if (t < 10.5) scene3();
|
|
358
|
+
else if (t < 10.95) roll(10.5, 0.45, scene3, scene4);
|
|
359
|
+
else scene4();
|
|
360
|
+
// status line (inverse video): the question lives here once asked
|
|
361
|
+
s.globalAlpha = 0.8; s.fillRect(36, TH - 44, TW - 72, 30); s.globalAlpha = 1;
|
|
362
|
+
s.globalCompositeOperation = 'destination-out'; s.font = '30px "VT323"'; s.letterSpacing = '1px';
|
|
363
|
+
const beat = t < 2.5 ? 'READY' : t < 6.3 ? 'DATA' : t < 10.7 ? 'TRAINING LOOP' : 'RESULTS';
|
|
364
|
+
const stl = ' NX-80 ' + (t > 1.9 ? ' Q: HOW DOES AI LEARN?' : '') + ' ' + beat;
|
|
365
|
+
s.fillText(stl, 46, TH - 44); s.lineWidth = 1; s.strokeText(stl, 46, TH - 44);
|
|
366
|
+
s.globalCompositeOperation = 'source-over'; s.letterSpacing = '0px';
|
|
367
|
+
};
|
|
368
|
+
crtCompose(ctx, W, H_, t, paint, 5, boost);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ================= SOUND: a green-screen terminal in a quiet room. Mains hum from the tube, a heavy buckling-spring
|
|
372
|
+
// keyboard for typed lines, soft relay ticks as the data log scrolls, an electron-beam crackle when lines are drawn,
|
|
373
|
+
// V-hold rolls as a sagging hum surge with static, and the terminal bell for the hits. Times copied from crtExplainer.
|
|
374
|
+
function crtSound(k) {
|
|
375
|
+
const END = 14.5;
|
|
376
|
+
const px = (x) => H.clamp(x / 1088) * 1.4 - 0.7; // tube x (0..1088) -> pan
|
|
377
|
+
const sq = (t, f, dur, gain, o) => k.tone(t, Object.assign({ f, type: 'square', dur, gain, a: 0.002, hold: dur * 0.6, filter: 'lowpass', f2: 1800 }, o));
|
|
378
|
+
// buckling-spring key: snap + low case thock
|
|
379
|
+
const key = (t, p = 0, g = 1) => { k.noise(t, { dur: 0.02, filter: 'bandpass', f: 2300, q: 1.6, gain: 0.22 * g, a: 0.001, pan: p, minor: true }); k.noise(t + 0.006, { dur: 0.05, color: 'brown', filter: 'lowpass', f: 600, gain: 0.3 * g, a: 0.002, pan: p, minor: true }); k.tone(t, { f: 150, dur: 0.04, gain: 0.08 * g, a: 0.001, pan: p, minor: true }); };
|
|
380
|
+
const enter = (t, p = 0) => { key(t, p, 1.4); k.noise(t + 0.03, { dur: 0.06, color: 'brown', filter: 'lowpass', f: 400, gain: 0.25, pan: p }); };
|
|
381
|
+
// typing a line: a typist's pace (~11 keys/s) over the stretch the characters appear
|
|
382
|
+
const typeLine = (t0, dur, p = 0) => { const r = H.rng(Math.round(t0 * 100)); for (let t = t0; t < t0 + dur - 0.02; t += 0.07 + r() * 0.04) key(t, p, 0.75 + r() * 0.35); };
|
|
383
|
+
// terminal output printing (no keyboard): a soft tick per few glyphs
|
|
384
|
+
const out = (t0, dur, p = 0) => { for (let t = t0; t < t0 + dur - 0.02; t += 0.09) k.noise(t, { dur: 0.012, filter: 'bandpass', f: 1200, q: 3, gain: 0.12, a: 0.001, pan: p, minor: true }); };
|
|
385
|
+
// electron beam tracing a line on the phosphor: thin crackle that follows the draw
|
|
386
|
+
const beam = (t, dur, p0 = 0, p1 = 0) => k.noise(t, { dur, filter: 'bandpass', f: [[0, 1400], [dur, 2000]], q: 3, gain: 0.07, a: dur * 0.2, hold: dur * 0.5, pan: (p0 + p1) / 2, minor: true });
|
|
387
|
+
// V-hold roll a -> a+d: hum sags and surges, static rides the blanking bar, raster locks with a thump
|
|
388
|
+
const roll = (a, d) => {
|
|
389
|
+
k.tone(a, { f: [[0, 62], [d * 0.5, 44], [d, 60]], type: 'sawtooth', dur: d + 0.08, gain: 0.08, a: d * 0.3, hold: d * 0.3, filter: 'lowpass', f2: 380 });
|
|
390
|
+
k.noise(a, { dur: d, color: 'pink', filter: 'bandpass', f: [[0, 500], [d * 0.5, 1600], [d, 400]], q: 1.1, gain: 0.08, a: d * 0.45 });
|
|
391
|
+
k.play('thud', a + d, { f: 85, gain: 0.25 });
|
|
392
|
+
};
|
|
393
|
+
// phosphor flash: high-voltage zap
|
|
394
|
+
const zap = (t, g = 1) => { k.noise(t, { dur: 0.14, filter: 'highpass', f: 1800, gain: 0.16 * g, a: 0.001 }); k.play('thud', t, { f: 95, gain: 0.5 * g }); };
|
|
395
|
+
// terminal bell (kept low: VT bells are mid-range)
|
|
396
|
+
const bell = (t, f, g = 1, p = 0) => { k.tone(t, { f, type: 'triangle', dur: 0.5, gain: 0.16 * g, a: 0.002, verb: 0.15, pan: p }); k.tone(t, { f: k.hz(f) * 2, dur: 0.25, gain: 0.04 * g, a: 0.002, pan: p }); };
|
|
397
|
+
|
|
398
|
+
k.bed('hum', 0, END, { gain: 0.55 });
|
|
399
|
+
k.bed('room', 0, END, { gain: 0.6 });
|
|
400
|
+
|
|
401
|
+
// ---- BEAT 1: user types the question (0.1, 32 cps, 20 chars), Enter, machine echoes it big (0.75, 30 cps, 18 chars)
|
|
402
|
+
typeLine(0.1, 0.63, -0.5);
|
|
403
|
+
enter(0.75, -0.3);
|
|
404
|
+
out(0.78, 0.57, -0.1);
|
|
405
|
+
k.play('thud', 1.4, { f: 70, gain: 0.2 }); // '====' rule
|
|
406
|
+
sq(1.9, 'A3', 0.06, 0.04, { pan: -0.4 }); // status line takes the question
|
|
407
|
+
// scroll down to the data screen (2.05-2.55): a smooth low slide
|
|
408
|
+
k.noise(2.05, { dur: 0.5, color: 'brown', filter: 'lowpass', f: [[0, 250], [0.25, 700], [0.5, 250]], gain: 0.12, a: 0.22 });
|
|
409
|
+
|
|
410
|
+
// ---- BEAT 2: '$ train ...' typed (2.1, 70 cps), Enter, log streams (a line every 0.085 s from 2.2)
|
|
411
|
+
typeLine(2.1, 0.45, -0.5);
|
|
412
|
+
enter(2.55, -0.5);
|
|
413
|
+
for (let i = 1; (2.2 + i * 0.085) < 6.1; i++) { const t = 2.2 + i * 0.085; k.noise(t, { dur: 0.01, filter: 'bandpass', f: 900, q: 4, gain: 0.1, a: 0.001, pan: -0.6, minor: true }); }
|
|
414
|
+
beam(2.2, 0.3, px(410), px(660)); // picture box draws
|
|
415
|
+
beam(2.5, 0.4, px(800), px(1050)); // model box draws
|
|
416
|
+
beam(2.6, 0.4, px(670), px(790)); // arrow into the model
|
|
417
|
+
sq(2.8, 'D3', 0.08, 0.05, { pan: px(950) }); // inverse ' MODEL ' tag
|
|
418
|
+
// each new example + label lands every 0.55 s: relay clunk
|
|
419
|
+
for (let t = 2.75; t < 6.1; t += 0.55) k.play('tock', t, { f: 260, gain: 0.45, pan: px(530), minor: true });
|
|
420
|
+
// EXAMPLES SEEN counter (2.9-5.7, accelerating): ticks closing in, at most 12/s
|
|
421
|
+
for (let t = 2.95; t < 5.65;) { k.noise(t, { dur: 0.016, filter: 'bandpass', f: 2000, q: 2, gain: 0.13, a: 0.001, pan: px(500), minor: true }); t += H.lerp(0.25, 1 / 12, (t - 2.9) / 2.8); }
|
|
422
|
+
bell(5.7, 'E4', 0.9, px(500)); // lands on 1,000,000
|
|
423
|
+
roll(6.1, 0.45);
|
|
424
|
+
|
|
425
|
+
// ---- BEAT 3: guess arrow (6.7-7.1), dog? (7.05)
|
|
426
|
+
beam(6.7, 0.4, px(260), px(460)); beam(6.95, 0.2, px(730), px(800));
|
|
427
|
+
sq(7.05, 'G3', 0.1, 0.05, { pan: px(860) });
|
|
428
|
+
// CHECK (7.5): the X, the inverse WRONG label and a phosphor flash at 7.55 -> bell clash + zap
|
|
429
|
+
sq(7.5, 'A3', 0.32, 0.1, { hold: 0.22, f2: 1400, pan: px(860) }); sq(7.5, 'Bb3', 0.32, 0.08, { hold: 0.22, f2: 1400, pan: px(860) });
|
|
430
|
+
zap(7.55, 1.2);
|
|
431
|
+
// ADJUST (8.35): error path drawn back (8.35-8.9); weights roll their digits 8.55-9.3
|
|
432
|
+
sq(8.35, 'D3', 0.06, 0.04, { pan: -0.2 });
|
|
433
|
+
beam(8.35, 0.55, px(870), px(600));
|
|
434
|
+
for (let t = 8.56; t < 9.3; t += 1 / 12) k.noise(t, { dur: 0.012, filter: 'bandpass', f: 1600, q: 3, gain: 0.12, a: 0.001, pan: px(560), minor: true });
|
|
435
|
+
sq(9.3, 'A3', 0.08, 0.04, { pan: px(560) }); // weights settle
|
|
436
|
+
sq(9.4, 'D3', 0.06, 0.04, { pan: -0.4 }); // GUESS again
|
|
437
|
+
sq(9.7, 'G3', 0.08, 0.045, { pan: px(860) }); // 'cat'
|
|
438
|
+
// [ OK ] + check (9.95): the correction, a clean two-note bell
|
|
439
|
+
bell(9.95, 'E4', 0.9, px(860)); bell(10.05, 'A4', 0.9, px(860));
|
|
440
|
+
roll(10.5, 0.45);
|
|
441
|
+
|
|
442
|
+
// ---- BEAT 4: 12 bars rise (11.0 + i*0.117), % climbs with them
|
|
443
|
+
k.withMinor(() => { for (let i = 0; i < 12; i++) sq(11.0 + i * (1.4 / 12), 160 * Math.pow(2, i / 10), 0.07, 0.045, { f2: 1000, pan: px(100 + i * 52) }); });
|
|
444
|
+
// 97% (12.45): inverse flip + phosphor flash, the biggest hit. bell chord over a deep thump and zap
|
|
445
|
+
const P = 12.45;
|
|
446
|
+
zap(P, 1.6); bell(P, 'C4', 1.8, px(820)); bell(P, 'E4', 1.5, px(820)); bell(P, 'G4', 1.5, px(820)); bell(P + 0.08, 'C5', 1.4, px(820));
|
|
447
|
+
k.tone(P, { f: 'C2', dur: 0.8, gain: 0.3, a: 0.004, hold: 0.15 });
|
|
448
|
+
beam(12.5, 0.3, px(750), px(640));
|
|
449
|
+
// ---- BEAT 5: takeaway typed (13.0, 55 cps, 33 chars -> 13.6) and lands with a soft resolving bell
|
|
450
|
+
typeLine(13.0, 0.6, -0.3);
|
|
451
|
+
bell(13.62, 'G3', 0.7); bell(13.74, 'C4', 0.9);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
registerStyle({
|
|
455
|
+
id: 'crt-terminal',
|
|
456
|
+
name: 'Phosphor Terminal',
|
|
457
|
+
category: 'Retro Digital',
|
|
458
|
+
description: 'A green P1-phosphor CRT: VT323 boot log with bloom and halation, curved scanlines, true per-pixel barrel distortion with RGB fringing at the edges, tube vignette, hum bar, and a glass face catching the room light inside a plastic bezel.',
|
|
459
|
+
motion: 'The tube powers on (dot, then a bright line that snaps open into a flashing raster that settles as the phosphor warms), then the boot log types at terminal speed, the trace sweeps, the greeting prints and the cursor blinks while a hum bar rolls.',
|
|
460
|
+
duration: 6,
|
|
461
|
+
fonts: ['26px "VT323"', '92px "VT323"', '700 15px "Space Mono"', '400 11px "Space Mono"'],
|
|
462
|
+
frameT: 5.8,
|
|
463
|
+
readsPixels: true,
|
|
464
|
+
explainerDuration: 14.5,
|
|
465
|
+
explainerNote: 'Explains as a live terminal session: the typed question scrolls away and lives on in the inverse-video status line, a labelled data log streams into a box-drawn MODEL, V-hold rolls carry us between screens, arrows and the error path are phosphor line-drawings, and counters are big VT323 numerals that flip in inverse video for emphasis.',
|
|
466
|
+
explainer(ctx, W, H_, t) { crtExplainer(ctx, W, H_, t); },
|
|
467
|
+
explainerSound(k) { crtSound(k); },
|
|
468
|
+
draw(ctx, W, H_, t) {
|
|
469
|
+
const nz = H.makeNoise(5);
|
|
470
|
+
// same tube pipeline as the explainer: bezel, barrel remap, bloom, glass
|
|
471
|
+
const paint = (s, TW, TH) => {
|
|
472
|
+
// ---------- 2. SOURCE SCREEN (flat, pre-distortion) in phosphor intensity ----------
|
|
473
|
+
s.shadowColor = '#fff'; s.shadowBlur = 3;
|
|
474
|
+
const ox = 64, oy = 36, lh = 28;
|
|
475
|
+
const cps = 200; // chars per second
|
|
476
|
+
let budget = Math.max(0, (t - 1.0) * cps), lastX = ox, lastY = oy, done = true;
|
|
477
|
+
s.font = '26px "VT323"';
|
|
478
|
+
for (let i = 0; i < LINES.length; i++) {
|
|
479
|
+
let [txt, kind] = LINES[i];
|
|
480
|
+
if (kind === 2) {
|
|
481
|
+
const prog = H.clamp((t - 1.55) / 0.9);
|
|
482
|
+
const n = 24, f = Math.round(prog * n);
|
|
483
|
+
txt = txt.replace('[BAR]', '[' + '█'.repeat(f) + '·'.repeat(n - f) + '] ' + String(Math.round(prog * 100)).padStart(3, ' ') + '%');
|
|
484
|
+
}
|
|
485
|
+
const shown = txt.slice(0, Math.floor(budget));
|
|
486
|
+
budget -= txt.length + 14; // a short pause at each line end
|
|
487
|
+
s.globalAlpha = kind === 1 && shown.endsWith('ok') ? 0.75 : 1;
|
|
488
|
+
s.globalAlpha = kind === 0 && i < 2 ? 0.8 : kind === 2 ? 0.62 : s.globalAlpha;
|
|
489
|
+
s.fillText(shown, ox, oy + i * lh);
|
|
490
|
+
if (shown.length < txt.length) { lastX = ox + s.measureText(shown).width; lastY = oy + i * lh; done = false; break; }
|
|
491
|
+
lastX = ox + s.measureText(shown).width; lastY = oy + i * lh;
|
|
492
|
+
}
|
|
493
|
+
s.globalAlpha = 1;
|
|
494
|
+
// big greeting, typed after the log
|
|
495
|
+
const big = 'HELLO. I AM AWAKE.';
|
|
496
|
+
if (done) {
|
|
497
|
+
const bt = Math.floor(Math.max(0, budget) * 0.16);
|
|
498
|
+
const bshown = big.slice(0, bt);
|
|
499
|
+
s.font = '92px "VT323"'; s.shadowBlur = 8;
|
|
500
|
+
s.fillText(bshown, ox, oy + 11 * lh + 14);
|
|
501
|
+
const bw = s.measureText(bshown).width;
|
|
502
|
+
if (bt >= big.length) {
|
|
503
|
+
s.font = '26px "VT323"'; s.shadowBlur = 3;
|
|
504
|
+
s.globalAlpha = 0.85; s.fillText('='.repeat(Math.round(s.measureText(big).width / 13 * 0.0 + 46)), ox, oy + 11 * lh + 100); s.globalAlpha = 1;
|
|
505
|
+
s.fillText('> ', ox, oy + 11 * lh + 136);
|
|
506
|
+
lastX = ox + 26; lastY = oy + 11 * lh + 136;
|
|
507
|
+
} else { lastX = ox + bw + 6; lastY = oy + 11 * lh + 14; }
|
|
508
|
+
}
|
|
509
|
+
// right-hand panel: cortex activity trace (vector-ish box with corner brackets)
|
|
510
|
+
{
|
|
511
|
+
const px0 = 700, py0 = 44, pw = 330, ph = 210;
|
|
512
|
+
s.globalAlpha = 0.9; s.font = '26px "VT323"'; s.shadowBlur = 3;
|
|
513
|
+
s.fillText('CORTEX ACTIVITY', px0 + 12, py0 - 2);
|
|
514
|
+
s.strokeStyle = '#fff'; s.lineWidth = 2;
|
|
515
|
+
const br = (x, y, dx, dy) => { s.beginPath(); s.moveTo(x + dx * 18, y); s.lineTo(x, y); s.lineTo(x, y + dy * 18); s.stroke(); };
|
|
516
|
+
br(px0, py0 + 26, 1, 1); br(px0 + pw, py0 + 26, -1, 1); br(px0, py0 + ph, 1, -1); br(px0 + pw, py0 + ph, -1, -1);
|
|
517
|
+
s.globalAlpha = 0.25; s.lineWidth = 1;
|
|
518
|
+
for (let gx = 1; gx < 6; gx++) { s.beginPath(); for (let gy = py0 + 34; gy < py0 + ph - 6; gy += 6) { s.moveTo(px0 + gx * pw / 6, gy); s.lineTo(px0 + gx * pw / 6, gy + 2); } s.stroke(); }
|
|
519
|
+
s.globalAlpha = 1; s.lineWidth = 2.2; s.shadowBlur = 6;
|
|
520
|
+
const prog = H.clamp((t - 1.2) / 2.6);
|
|
521
|
+
s.beginPath();
|
|
522
|
+
for (let x = 0; x <= pw - 20; x += 2) {
|
|
523
|
+
if (x / (pw - 20) > prog) break;
|
|
524
|
+
const k = x / (pw - 20);
|
|
525
|
+
const amp = 6 + 46 * H.smooth(0.55, 0.95, k);
|
|
526
|
+
const y = py0 + 26 + (ph - 26) / 2 + Math.sin(x * 0.11 + t * 6) * amp * 0.6 + nz.n2(x * 0.06, t) * amp;
|
|
527
|
+
if (x === 0) s.moveTo(px0 + 10 + x, y); else s.lineTo(px0 + 10 + x, y);
|
|
528
|
+
}
|
|
529
|
+
s.stroke();
|
|
530
|
+
s.shadowBlur = 3; s.font = '26px "VT323"';
|
|
531
|
+
s.fillText('SIGNAL ' + String(Math.round(prog * 97 + 2)).padStart(3, ' ') + '% T+' + t.toFixed(2), px0 + 12, py0 + ph + 8);
|
|
532
|
+
s.globalAlpha = 1;
|
|
533
|
+
}
|
|
534
|
+
// block cursor (blinks at ~2Hz)
|
|
535
|
+
if (Math.floor(t * 2.2) % 2 === 0) { s.shadowBlur = 6; const big2 = lastY > oy + 11 * lh && lastY < oy + 11 * lh + 40; s.fillRect(lastX + 2, lastY + 3, big2 ? 40 : 13, big2 ? 74 : 22); }
|
|
536
|
+
s.shadowBlur = 0;
|
|
537
|
+
// status line at bottom (inverse video)
|
|
538
|
+
s.globalAlpha = 0.8; s.fillRect(36, TH - 44, TW - 72, 30); s.globalAlpha = 1;
|
|
539
|
+
s.globalCompositeOperation = 'destination-out'; s.font = '30px "VT323"'; s.letterSpacing = '1px';
|
|
540
|
+
const stl = ' NX-80 SESSION 01 9600 BAUD ONLINE ' + '00:0' + Math.floor(t) + ':' + String(Math.floor(t * 60) % 60).padStart(2, '0') + ' CAPS';
|
|
541
|
+
s.fillText(stl, 46, TH - 44); s.lineWidth = 1; s.strokeText(stl, 46, TH - 44);
|
|
542
|
+
s.globalCompositeOperation = 'source-over'; s.letterSpacing = '0px';
|
|
543
|
+
};
|
|
544
|
+
crtCompose(ctx, W, H_, t, paint, t - 0.25, 1);
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 45,
|
|
3
|
+
"name": "Phosphor Terminal",
|
|
4
|
+
"nameZh": "荧光终端",
|
|
5
|
+
"family": "Retro Digital",
|
|
6
|
+
"familyZh": "复古数字",
|
|
7
|
+
"description": "A green P1-phosphor CRT: VT323 boot log with bloom and halation, curved scanlines, true per-pixel barrel distortion with RGB fringing at the edges, tube vignette, hum bar, and a glass face catching the room light inside a plastic bezel.",
|
|
8
|
+
"motionNotes": "Motion: The tube powers on (dot, then a bright line that snaps open into a flashing raster that settles as the phosphor warms), then the boot log types at terminal speed, the trace sweeps, the greeting prints and the cursor blinks while a hum bar rolls.",
|
|
9
|
+
"explainerNotes": "Explainer: Explains as a live terminal session: the typed question scrolls away and lives on in the inverse-video status line, a labelled data log streams into a box-drawn MODEL, V-hold rolls carry us between screens, arrows and the error path are phosphor line-drawings, and counters are big VT323 numerals that flip in inverse video for emphasis.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "crt-terminal",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|