@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,627 @@
|
|
|
1
|
+
{
|
|
2
|
+
// ---------- 1980 XY vector monitor: thin beams on black, additive bloom, vertex dwell dots, stroke font ----------
|
|
3
|
+
const GLYPH = {
|
|
4
|
+
A: [[[0, 6], [0, 2], [2, 0], [4, 2], [4, 6]], [[0, 4], [4, 4]]],
|
|
5
|
+
B: [[[0, 6], [0, 0], [3, 0], [4, 1], [4, 2], [3, 3], [0, 3]], [[3, 3], [4, 4], [4, 5], [3, 6], [0, 6]]],
|
|
6
|
+
C: [[[4, 0], [0, 0], [0, 6], [4, 6]]],
|
|
7
|
+
D: [[[0, 0], [2, 0], [4, 2], [4, 4], [2, 6], [0, 6], [0, 0]]],
|
|
8
|
+
E: [[[4, 0], [0, 0], [0, 6], [4, 6]], [[0, 3], [3, 3]]],
|
|
9
|
+
F: [[[4, 0], [0, 0], [0, 6]], [[0, 3], [3, 3]]],
|
|
10
|
+
G: [[[4, 1], [4, 0], [0, 0], [0, 6], [4, 6], [4, 3], [2, 3]]],
|
|
11
|
+
H: [[[0, 0], [0, 6]], [[4, 0], [4, 6]], [[0, 3], [4, 3]]],
|
|
12
|
+
I: [[[0, 0], [4, 0]], [[2, 0], [2, 6]], [[0, 6], [4, 6]]],
|
|
13
|
+
J: [[[4, 0], [4, 6], [2, 6], [0, 4]]],
|
|
14
|
+
K: [[[0, 0], [0, 6]], [[4, 0], [0, 3], [4, 6]]],
|
|
15
|
+
L: [[[0, 0], [0, 6], [4, 6]]],
|
|
16
|
+
M: [[[0, 6], [0, 0], [2, 2], [4, 0], [4, 6]]],
|
|
17
|
+
N: [[[0, 6], [0, 0], [4, 6], [4, 0]]],
|
|
18
|
+
O: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]]],
|
|
19
|
+
P: [[[0, 6], [0, 0], [4, 0], [4, 3], [0, 3]]],
|
|
20
|
+
Q: [[[0, 0], [4, 0], [4, 4], [2, 6], [0, 6], [0, 0]], [[2, 4], [4, 6]]],
|
|
21
|
+
R: [[[0, 6], [0, 0], [4, 0], [4, 3], [0, 3], [4, 6]]],
|
|
22
|
+
S: [[[4, 0], [0, 0], [0, 3], [4, 3], [4, 6], [0, 6]]],
|
|
23
|
+
T: [[[0, 0], [4, 0]], [[2, 0], [2, 6]]],
|
|
24
|
+
U: [[[0, 0], [0, 6], [4, 6], [4, 0]]],
|
|
25
|
+
V: [[[0, 0], [2, 6], [4, 0]]],
|
|
26
|
+
W: [[[0, 0], [0, 6], [2, 4], [4, 6], [4, 0]]],
|
|
27
|
+
X: [[[0, 0], [4, 6]], [[4, 0], [0, 6]]],
|
|
28
|
+
Y: [[[0, 0], [2, 2], [4, 0]], [[2, 2], [2, 6]]],
|
|
29
|
+
Z: [[[0, 0], [4, 0], [0, 6], [4, 6]]],
|
|
30
|
+
0: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]]],
|
|
31
|
+
1: [[[2, 0], [2, 6]]],
|
|
32
|
+
2: [[[0, 0], [4, 0], [4, 3], [0, 3], [0, 6], [4, 6]]],
|
|
33
|
+
3: [[[0, 0], [4, 0], [4, 6], [0, 6]], [[0, 3], [4, 3]]],
|
|
34
|
+
4: [[[0, 0], [0, 3], [4, 3]], [[4, 0], [4, 6]]],
|
|
35
|
+
5: [[[4, 0], [0, 0], [0, 3], [4, 3], [4, 6], [0, 6]]],
|
|
36
|
+
6: [[[0, 0], [0, 6], [4, 6], [4, 3], [0, 3]]],
|
|
37
|
+
7: [[[0, 0], [4, 0], [4, 6]]],
|
|
38
|
+
8: [[[0, 0], [4, 0], [4, 6], [0, 6], [0, 0]], [[0, 3], [4, 3]]],
|
|
39
|
+
9: [[[4, 3], [0, 3], [0, 0], [4, 0], [4, 6]]],
|
|
40
|
+
'-': [[[1, 3], [3, 3]]],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ================= EXPLAINER: "How does AI learn?" as a vector-HUD sequence =================
|
|
44
|
+
const EXG = Object.assign({}, GLYPH, {
|
|
45
|
+
'?': [[[0, 1], [1, 0], [3, 0], [4, 1], [4, 2], [2, 3.5], [2, 4.6]], [[2, 5.7], [2, 6]]],
|
|
46
|
+
'%': [[[0, 6], [4, 0]], [[0, 0], [1.2, 0], [1.2, 1.2], [0, 1.2], [0, 0]], [[2.8, 4.8], [4, 4.8], [4, 6], [2.8, 6], [2.8, 4.8]]],
|
|
47
|
+
',': [[[2, 5.2], [1.4, 7]]],
|
|
48
|
+
'.': [[[2, 5.7], [2, 6]]],
|
|
49
|
+
':': [[[2, 1.5], [2, 2]], [[2, 4.5], [2, 5]]],
|
|
50
|
+
'>': [[[0, 0], [4, 3], [0, 6]]],
|
|
51
|
+
'~': [[[0, 3], [4, 3]], [[2.5, 1.4], [4, 3], [2.5, 4.6]]], // used as a right arrow glyph
|
|
52
|
+
});
|
|
53
|
+
// ---------- shared beam renderer (hero + explainer use the same passes) ----------
|
|
54
|
+
// Beam core layer (1.7px + dwell dots), fat halo layer (5px). Wide halos are blurred at half resolution (cheaper,
|
|
55
|
+
// identical look), then the tube glass is laid over: faint P31 face glow, a soft diagonal reflection of the cabinet,
|
|
56
|
+
// rounded-tube corner falloff and the vignette.
|
|
57
|
+
const glassCache = {};
|
|
58
|
+
function glassLayer(W, H_) {
|
|
59
|
+
const key = W + 'x' + H_;
|
|
60
|
+
if (glassCache[key]) return glassCache[key];
|
|
61
|
+
const G = H.layer(W, H_), g = G.getContext('2d');
|
|
62
|
+
g.fillStyle = '#000'; g.fillRect(0, 0, W, H_); // opaque, drawn in 'screen' mode so black = no change
|
|
63
|
+
// faint glow of the phosphor face (the black is lit glass, not a void)
|
|
64
|
+
const rg = g.createRadialGradient(W * 0.5, H_ * 0.52, 0, W * 0.5, H_ * 0.52, W * 0.62);
|
|
65
|
+
rg.addColorStop(0, 'rgba(20,40,30,0.55)'); rg.addColorStop(0.6, 'rgba(12,26,20,0.3)'); rg.addColorStop(1, 'rgba(0,0,0,0)');
|
|
66
|
+
g.fillStyle = rg; g.fillRect(0, 0, W, H_);
|
|
67
|
+
// tube glare up-left (as the hero had) + a soft diagonal reflection band of the cabinet
|
|
68
|
+
const gl = g.createRadialGradient(W * 0.3, H_ * 0.2, 0, W * 0.3, H_ * 0.2, W * 0.5); gl.addColorStop(0, 'rgba(120,160,140,0.05)'); gl.addColorStop(1, 'rgba(0,0,0,0)');
|
|
69
|
+
g.fillStyle = gl; g.fillRect(0, 0, W, H_);
|
|
70
|
+
g.save(); g.translate(W * 0.62, H_ * 0.3); g.rotate(-0.55);
|
|
71
|
+
const band = g.createLinearGradient(0, -90, 0, 90);
|
|
72
|
+
band.addColorStop(0, 'rgba(150,190,170,0)'); band.addColorStop(0.45, 'rgba(150,190,170,0.04)'); band.addColorStop(0.55, 'rgba(150,190,170,0.05)'); band.addColorStop(1, 'rgba(150,190,170,0)');
|
|
73
|
+
g.fillStyle = band; g.fillRect(-W, -90, W * 2, 180); g.restore();
|
|
74
|
+
// fine phosphor-screen grain (static, very faint)
|
|
75
|
+
const gr = H.rng(1981), id = g.getImageData(0, 0, W, H_), d = id.data;
|
|
76
|
+
for (let i = 0; i < d.length; i += 4) { const v = gr() * 3.5; d[i] += v * 0.6; d[i + 1] += v; d[i + 2] += v * 0.8; }
|
|
77
|
+
g.putImageData(id, 0, 0);
|
|
78
|
+
// rounded tube corners: the curved faceplate falls off into the bezel
|
|
79
|
+
const M = H.layer(W, H_), m = M.getContext('2d');
|
|
80
|
+
m.fillStyle = '#000'; m.fillRect(0, 0, W, H_);
|
|
81
|
+
m.globalCompositeOperation = 'destination-out'; m.filter = 'blur(18px)';
|
|
82
|
+
m.beginPath(); m.roundRect(10, 8, W - 20, H_ - 16, 90); m.fill();
|
|
83
|
+
m.globalCompositeOperation = 'source-over'; m.filter = 'none';
|
|
84
|
+
H.vignette(m, W, H_, 0.6); // baked: same vignette the frame used to draw live
|
|
85
|
+
const out = { glass: G, bezel: M };
|
|
86
|
+
return (glassCache[key] = out);
|
|
87
|
+
}
|
|
88
|
+
let halfA = null, halfB = null, halfT = null;
|
|
89
|
+
function renderBeams(ctx, W, H_, strokes, r, flick, jit = 0.2) {
|
|
90
|
+
const gls = glassLayer(W, H_);
|
|
91
|
+
ctx.drawImage(gls.glass, 0, 0); // opaque lit-glass base (black + faint face glow, glare, grain)
|
|
92
|
+
const beam = H.layer(W, H_), b = beam.getContext('2d');
|
|
93
|
+
b.lineCap = 'round'; b.lineJoin = 'round'; b.globalCompositeOperation = 'lighter';
|
|
94
|
+
const fat = H.layer(W, H_), fb = fat.getContext('2d');
|
|
95
|
+
fb.lineCap = 'round'; fb.lineJoin = 'round'; fb.globalCompositeOperation = 'lighter';
|
|
96
|
+
strokes.forEach((s) => {
|
|
97
|
+
// long strokes are swept faster -> dimmer; per-stroke beam jitter
|
|
98
|
+
let L = 0; for (let i = 1; i < s.pts.length; i++) L += Math.hypot(s.pts[i][0] - s.pts[i - 1][0], s.pts[i][1] - s.pts[i - 1][1]);
|
|
99
|
+
const inten = s.a * flick * (1 - jit + jit * r()) * H.clamp(1.25 - L / 2600, 0.55, 1);
|
|
100
|
+
const [cr, cg, cb] = s.col;
|
|
101
|
+
b.strokeStyle = `rgba(${cr},${cg},${cb},${inten})`; b.lineWidth = 1.7;
|
|
102
|
+
b.beginPath(); s.pts.forEach(([x, y], i) => (i ? b.lineTo(x + (r() - 0.5) * 0.4, y + (r() - 0.5) * 0.4) : b.moveTo(x, y))); b.stroke();
|
|
103
|
+
// dwell dots at vertices (beam pauses = brighter)
|
|
104
|
+
if (!s.ghost) {
|
|
105
|
+
b.fillStyle = `rgba(${Math.min(255, cr + 60)},${Math.min(255, cg + 40)},${Math.min(255, cb + 60)},${inten * 0.9})`;
|
|
106
|
+
s.pts.forEach(([x, y]) => { b.beginPath(); b.arc(x, y, 1.6, 0, 7); b.fill(); });
|
|
107
|
+
}
|
|
108
|
+
if (s.ghost) return; // decaying phosphor is too dim to feed the halo
|
|
109
|
+
fb.strokeStyle = `rgba(${cr},${cg},${cb},${s.a * 0.55})`; fb.lineWidth = 5; fb.beginPath(); s.pts.forEach(([x, y], i) => (i ? fb.lineTo(x, y) : fb.moveTo(x, y))); fb.stroke();
|
|
110
|
+
});
|
|
111
|
+
// low-res copies for the wide halos: downsample first, then blur the small canvas (much cheaper than blurring
|
|
112
|
+
// at full size; a 22px/14px halo has no detail to lose)
|
|
113
|
+
const hw = W >> 2, hh = H_ >> 2;
|
|
114
|
+
if (!halfA || halfA.width !== hw) { halfA = H.layer(hw, hh); halfB = H.layer(hw, hh); halfT = H.layer(hw, hh); }
|
|
115
|
+
const ha = halfA.getContext('2d'), hb = halfB.getContext('2d'), ht = halfT.getContext('2d');
|
|
116
|
+
ht.clearRect(0, 0, hw, hh); ht.drawImage(fat, 0, 0, hw, hh);
|
|
117
|
+
ha.clearRect(0, 0, hw, hh); ha.filter = 'blur(5.5px)'; ha.drawImage(halfT, 0, 0); ha.filter = 'none';
|
|
118
|
+
ht.clearRect(0, 0, hw, hh); ht.globalCompositeOperation = 'lighter'; ht.drawImage(beam, 0, 0, hw, hh); ht.drawImage(beam, 0, 0, hw, hh); ht.globalCompositeOperation = 'source-over';
|
|
119
|
+
hb.clearRect(0, 0, hw, hh); hb.filter = 'blur(3.5px)'; hb.drawImage(halfT, 0, 0); hb.filter = 'none';
|
|
120
|
+
ctx.save(); ctx.globalCompositeOperation = 'lighter';
|
|
121
|
+
ctx.globalAlpha = 0.75; ctx.drawImage(halfA, 0, 0, W, H_);
|
|
122
|
+
ctx.filter = 'blur(8px)'; ctx.globalAlpha = 0.6; ctx.drawImage(fat, 0, 0);
|
|
123
|
+
ctx.filter = 'none'; ctx.globalAlpha = 0.9; ctx.drawImage(halfB, 0, 0, W, H_);
|
|
124
|
+
ctx.filter = 'blur(4px)'; ctx.globalAlpha = 1; ctx.drawImage(beam, 0, 0);
|
|
125
|
+
ctx.filter = 'blur(1.5px)'; ctx.drawImage(beam, 0, 0);
|
|
126
|
+
ctx.filter = 'none'; ctx.globalAlpha = 0.9; ctx.drawImage(beam, 0, 0);
|
|
127
|
+
// white-hot centre on top of the core lines
|
|
128
|
+
ctx.globalAlpha = 0.35; ctx.filter = 'brightness(1.6)'; ctx.drawImage(beam, 0, 0);
|
|
129
|
+
ctx.restore();
|
|
130
|
+
ctx.drawImage(gls.bezel, 0, 0);
|
|
131
|
+
}
|
|
132
|
+
const exBeams = (ctx, W, H_, strokes, r, flick) => renderBeams(ctx, W, H_, strokes, r, flick, 0.2);
|
|
133
|
+
|
|
134
|
+
function vecExplainer(ctx, W, H_, t) {
|
|
135
|
+
const r = H.rng(77 + Math.floor(t * 30));
|
|
136
|
+
const GREEN = [110, 255, 120], RED = [255, 70, 50], WHITE = [210, 255, 220];
|
|
137
|
+
const flick = 0.93 + 0.07 * r();
|
|
138
|
+
const cx = W / 2, cyc = H_ / 2;
|
|
139
|
+
const strokes = [];
|
|
140
|
+
let REV = 1, ZOOM = 1, GHOST = false;
|
|
141
|
+
const cut = (pts, f) => {
|
|
142
|
+
if (f >= 1) return pts; if (f <= 0) return [];
|
|
143
|
+
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]);
|
|
144
|
+
let left = L * f; const out = [pts[0]];
|
|
145
|
+
for (let i = 1; i < pts.length; i++) {
|
|
146
|
+
const d = Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
|
|
147
|
+
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; }
|
|
148
|
+
out.push(pts[i]); left -= d;
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
};
|
|
152
|
+
const poly = (pts, col = GREEN, a = 1) => {
|
|
153
|
+
pts = cut(pts, REV);
|
|
154
|
+
if (ZOOM !== 1) pts = pts.map(([x, y]) => [cx + (x - cx) * ZOOM, cyc + (y - cyc) * ZOOM]);
|
|
155
|
+
if (pts.length > 1 && a > 0.01) strokes.push({ pts, col, a, ghost: GHOST });
|
|
156
|
+
};
|
|
157
|
+
// phosphor persistence (P31 decay): moving strokes leave a fading smear of copies over the last ~50 ms
|
|
158
|
+
const LAGS = [[0.048, 0.07], [0.036, 0.12], [0.024, 0.2], [0.012, 0.32]];
|
|
159
|
+
const trail = (fn) => { GHOST = true; LAGS.forEach(([lag, ga]) => fn(t - lag, ga)); GHOST = false; fn(t, 1); };
|
|
160
|
+
const ph = (a, d) => H.ease.inOut(H.clamp((t - a) / d));
|
|
161
|
+
const text = (str, x, y, sz, col, a = 1, track = 1.6) => {
|
|
162
|
+
let px = x; const nC = REV * str.length, rev0 = REV;
|
|
163
|
+
for (let ci = 0; ci < str.length; ci++) {
|
|
164
|
+
const g = EXG[str[ci]]; REV = H.clamp(nC - ci);
|
|
165
|
+
if (g && REV > 0) g.forEach((pl) => poly(pl.map(([gx, gy]) => [px + gx * sz, y + gy * sz]), col, a));
|
|
166
|
+
px += sz * 4 + sz * track;
|
|
167
|
+
}
|
|
168
|
+
REV = rev0; return px - x;
|
|
169
|
+
};
|
|
170
|
+
const tw = (str, sz, track = 1.6) => str.length * (sz * 4 + sz * track) - sz * track;
|
|
171
|
+
const ctext = (str, x, y, sz, col, a, track) => text(str, x - tw(str, sz, track) / 2, y, sz, col, a, track);
|
|
172
|
+
const arrow = (x1, y1, x2, y2, col, a = 1) => {
|
|
173
|
+
const ang = Math.atan2(y2 - y1, x2 - x1), L = Math.hypot(x2 - x1, y2 - y1), f = REV;
|
|
174
|
+
const ex = x1 + (x2 - x1) * f, ey = y1 + (y2 - y1) * f;
|
|
175
|
+
const r0 = REV; REV = 1;
|
|
176
|
+
poly([[x1, y1], [ex, ey]], col, a);
|
|
177
|
+
if (f > 0.05) poly([[ex + Math.cos(ang + 2.6) * 16, ey + Math.sin(ang + 2.6) * 16], [ex, ey], [ex + Math.cos(ang - 2.6) * 16, ey + Math.sin(ang - 2.6) * 16]], col, a);
|
|
178
|
+
REV = r0; return L;
|
|
179
|
+
};
|
|
180
|
+
const box = (x, y, w, h, col, a = 1) => poly([[x, y], [x + w, y], [x + w, y + h], [x, y + h], [x, y]], col, a);
|
|
181
|
+
const brackets = (x, y, w, h, s, col, a = 1) => {
|
|
182
|
+
poly([[x, y + s], [x, y], [x + s, y]], col, a); poly([[x + w - s, y], [x + w, y], [x + w, y + s]], col, a);
|
|
183
|
+
poly([[x, y + h - s], [x, y + h], [x + s, y + h]], col, a); poly([[x + w - s, y + h], [x + w, y + h], [x + w, y + h - s]], col, a);
|
|
184
|
+
};
|
|
185
|
+
const shape = (pts, x, y, s, col, a = 1, close = true) => poly((close ? [...pts, pts[0]] : pts).map(([px, py]) => [x + px * s, y + py * s]), col, a);
|
|
186
|
+
const cat = (x, y, s, col, a = 1) => {
|
|
187
|
+
shape([[-1, -0.1], [-0.85, -1], [-0.35, -0.55], [0.35, -0.55], [0.85, -1], [1, -0.1], [0.8, 0.6], [0, 0.9], [-0.8, 0.6]], x, y, s, col, a);
|
|
188
|
+
shape([[-0.45, -0.1], [-0.25, -0.1]], x, y, s, col, a, false); shape([[0.25, -0.1], [0.45, -0.1]], x, y, s, col, a, false);
|
|
189
|
+
shape([[-0.1, 0.25], [0.1, 0.25], [0, 0.38], [-0.1, 0.25]], x, y, s, col, a, false);
|
|
190
|
+
shape([[-0.3, 0.35], [-1.1, 0.2]], x, y, s, col, a, false); shape([[0.3, 0.35], [1.1, 0.2]], x, y, s, col, a, false);
|
|
191
|
+
};
|
|
192
|
+
const dog = (x, y, s, col, a = 1) => {
|
|
193
|
+
shape([[-0.6, -0.75], [0.6, -0.75], [0.72, 0.3], [0.3, 0.85], [-0.3, 0.85], [-0.72, 0.3]], x, y, s, col, a);
|
|
194
|
+
shape([[-0.6, -0.75], [-1.05, -0.45], [-0.95, 0.35], [-0.66, 0.1]], x, y, s, col, a, false);
|
|
195
|
+
shape([[0.6, -0.75], [1.05, -0.45], [0.95, 0.35], [0.66, 0.1]], x, y, s, col, a, false);
|
|
196
|
+
shape([[-0.3, -0.2], [-0.15, -0.2]], x, y, s, col, a, false); shape([[0.15, -0.2], [0.3, -0.2]], x, y, s, col, a, false);
|
|
197
|
+
shape([[-0.15, 0.3], [0.15, 0.3], [0, 0.45]], x, y, s, col, a);
|
|
198
|
+
};
|
|
199
|
+
const check = (x, y, s, col, a = 1) => poly([[x - s, y], [x - s * 0.3, y + s * 0.7], [x + s, y - s * 0.9]], col, a);
|
|
200
|
+
const cross = (x, y, s, col, a = 1) => { poly([[x - s, y - s], [x + s, y + s]], col, a); poly([[x + s, y - s], [x - s, y + s]], col, a); };
|
|
201
|
+
// wireframe icosahedron "model core" in 2D (weak perspective), optional vertex wobble = weights moving
|
|
202
|
+
const phi = (1 + Math.sqrt(5)) / 2;
|
|
203
|
+
const icoV = [[-1, phi, 0], [1, phi, 0], [-1, -phi, 0], [1, -phi, 0], [0, -1, phi], [0, 1, phi], [0, -1, -phi], [0, 1, -phi], [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1], [-phi, 0, 1]];
|
|
204
|
+
const icoE = []; for (let i = 0; i < 12; i++) for (let j = i + 1; j < 12; j++) { const d = Math.hypot(icoV[i][0] - icoV[j][0], icoV[i][1] - icoV[j][1], icoV[i][2] - icoV[j][2]); if (Math.abs(d - 2) < 0.01) icoE.push([i, j]); }
|
|
205
|
+
const core = (x, y, s, col, a = 1, wob = 0, rev = 1, tt = t) => {
|
|
206
|
+
const ca = tt * 0.6, cb = tt * 0.37;
|
|
207
|
+
const P = icoV.map((v, k) => {
|
|
208
|
+
let [vx, vy, vz] = v.map((q) => q + (wob ? Math.sin(tt * 9 + k * 2.1) * wob : 0));
|
|
209
|
+
let y1 = vy * Math.cos(cb) - vz * Math.sin(cb), z1 = vy * Math.sin(cb) + vz * Math.cos(cb);
|
|
210
|
+
let x2 = vx * Math.cos(ca) - z1 * Math.sin(ca), z2 = vx * Math.sin(ca) + z1 * Math.cos(ca);
|
|
211
|
+
const pz = 1 / (1 + z2 * 0.08);
|
|
212
|
+
return [x + x2 * s * pz, y + y1 * s * pz];
|
|
213
|
+
});
|
|
214
|
+
const r0 = REV, k = rev * icoE.length;
|
|
215
|
+
icoE.forEach(([i, j], e) => { REV = H.clamp(k - e); if (REV > 0) poly([P[i], P[j]], col, a); });
|
|
216
|
+
REV = r0;
|
|
217
|
+
};
|
|
218
|
+
// the hero's mountain range (same seed and silhouette) standing on the y=520 ground line
|
|
219
|
+
const MTN = []; { const rs = H.rng(42); for (let i = 0; i <= 26; i++) { const x = -200 + i * ((W + 400) / 26); const h = (i % 2 ? 10 + rs() * 30 : 40 + rs() * 70); MTN.push([x, 520 - h * (i > 9 && i < 15 ? 0.45 : 1)]); } }
|
|
220
|
+
const fmt = (n) => String(Math.floor(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
221
|
+
const streaks = (k, a) => { const rs = H.rng(5); for (let i = 0; i < 46; i++) { const ang = rs() * 6.283, d0 = 40 + rs() * 300; const d1 = d0 + 60 + k * 900; poly([[cx + Math.cos(ang) * d0 * (1 + k * 2), cyc + Math.sin(ang) * d0 * (1 + k * 2)], [cx + Math.cos(ang) * d1, cyc + Math.sin(ang) * d1]], GREEN, a); } };
|
|
222
|
+
|
|
223
|
+
// ---------- persistent HUD: the question, once it has shrunk into the corner ----------
|
|
224
|
+
const hdrK = ph(1.7, 0.6);
|
|
225
|
+
{
|
|
226
|
+
const sz = H.lerp(10, 4, hdrK), x = H.lerp(cx - tw('HOW DOES AI LEARN?', 10) / 2, 40, hdrK), y = H.lerp(290, 34, hdrK);
|
|
227
|
+
REV = H.clamp((t - 0.15) / 0.9);
|
|
228
|
+
text('HOW DOES AI LEARN?', x, y, sz, hdrK > 0.5 ? RED : GREEN, 1);
|
|
229
|
+
REV = 1;
|
|
230
|
+
if (hdrK >= 1) poly([[40, 70], [40 + tw('HOW DOES AI LEARN?', 4), 70]], RED, 0.6);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ---------- BEAT 1 -> 2: the model core appears, then the DATA stream ----------
|
|
234
|
+
if (t < 6.4) {
|
|
235
|
+
ZOOM = t > 5.8 ? 1 + Math.pow((t - 5.8) / 0.6, 2) * 9 : 1; // warp INTO the core
|
|
236
|
+
const coreX = cx + 180, coreY = 360;
|
|
237
|
+
REV = ph(0.9, 0.5); poly([[0, 520], [W, 520]], GREEN, 0.5);
|
|
238
|
+
poly(MTN, GREEN, 0.2); REV = 1;
|
|
239
|
+
trail((tt, ga) => core(coreX, coreY, 70, WHITE, ga, 0, ph(1.95, 1.0), tt));
|
|
240
|
+
if (t > 2.4) { REV = ph(2.4, 0.4); poly([[coreX, coreY + 130], [coreX, coreY + 168]], GREEN, 0.8); REV = ph(2.6, 0.4); ctext('MODEL', coreX, coreY + 178, 6, GREEN, 1); REV = 1; }
|
|
241
|
+
// the stream arrow
|
|
242
|
+
if (t > 2.6) { REV = ph(2.6, 0.5); arrow(90, 520, coreX - 120, 470, GREEN, 0.7); REV = ph(2.9, 0.4); text('TRAINING DATA', 100, 540, 5, GREEN, 0.95); REV = 1; }
|
|
243
|
+
// labelled examples fly along the arrow into the core
|
|
244
|
+
for (let i = 0; i < 9; i++) {
|
|
245
|
+
const t0 = 2.6 + i * 0.34;
|
|
246
|
+
if (t < t0 || t > t0 + 1.5) continue;
|
|
247
|
+
const isCat = (i * 7) % 3 !== 0;
|
|
248
|
+
trail((tt, ga) => {
|
|
249
|
+
const k = (tt - t0) / 1.5; if (k < 0 || k > 1) return;
|
|
250
|
+
const x = H.lerp(-80, coreX - 40, k), y = H.lerp(400, coreY, Math.pow(k, 2)) - Math.sin(k * Math.PI) * 30;
|
|
251
|
+
const s = 46 * (1 - Math.pow(k, 3) * 0.85);
|
|
252
|
+
box(x - s * 1.25, y - s * 1.25, s * 2.5, s * 2.5, GREEN, 0.75 * ga);
|
|
253
|
+
(isCat ? cat : dog)(x, y - s * 0.12, s * 0.8, GREEN, 0.95 * ga);
|
|
254
|
+
if (s > 20 && ga === 1) text(isCat ? 'CAT' : 'DOG', x - tw('CAT', s / 9) / 2, y + s * 1.4, s / 9, isCat ? GREEN : WHITE, 1);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
// counter readout
|
|
258
|
+
if (t > 2.8) {
|
|
259
|
+
const n = 1e6 * Math.pow(H.clamp((t - 2.9) / 2.6), 2.2);
|
|
260
|
+
REV = ph(2.8, 0.3); box(40, 90, 560, 130, RED, 0.7); text('EXAMPLES SEEN', 60, 104, 4.2, RED, 0.95); REV = 1;
|
|
261
|
+
text(fmt(n), 60, 140, 11, GREEN, 1, 1.3);
|
|
262
|
+
}
|
|
263
|
+
if (t > 5.8) streaks((t - 5.8) / 0.6, 0.6);
|
|
264
|
+
ZOOM = 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ---------- BEAT 3: GUESS -> CHECK -> ADJUST loop ----------
|
|
268
|
+
if (t >= 6.4 && t < 10.9) {
|
|
269
|
+
ZOOM = t < 6.75 ? Math.pow((t - 6.4) / 0.35, 0.6) : t > 10.5 ? 1 - Math.pow((t - 10.5) / 0.4, 2) : 1;
|
|
270
|
+
if (t < 6.8) streaks(1 - (t - 6.4) / 0.4, 0.4);
|
|
271
|
+
const picX = 260, picY = 380, coreX = 840, coreY = 360;
|
|
272
|
+
// loop indicator across the top: GUESS > CHECK > ADJUST, with return arrow
|
|
273
|
+
const phase = t < 7.5 ? 0 : t < 8.4 ? 1 : t < 9.4 ? 2 : 0;
|
|
274
|
+
const items = ['GUESS', 'CHECK', 'ADJUST'], ix = [400, 640, 890];
|
|
275
|
+
items.forEach((s, i) => {
|
|
276
|
+
const on = i === phase;
|
|
277
|
+
ctext(s, ix[i], 104, 5, on ? WHITE : GREEN, on ? 1 : 0.5);
|
|
278
|
+
if (on) brackets(ix[i] - tw(s, 5) / 2 - 16, 90, tw(s, 5) + 32, 58, 10, WHITE, 1);
|
|
279
|
+
if (i < 2) { REV = 1; arrow(ix[i] + tw(s, 5) / 2 + 12, 120, ix[i + 1] - tw(items[i + 1], 5) / 2 - 12, 120, GREEN, 0.5); }
|
|
280
|
+
});
|
|
281
|
+
poly([[ix[2], 152], [ix[2], 172], [ix[0], 172]], GREEN, 0.45); arrow(ix[0], 172, ix[0], 154, GREEN, 0.45);
|
|
282
|
+
// dim horizon + mountain range behind the loop (broken where the NEW PICTURE caption sits)
|
|
283
|
+
poly([[0, 520], [picX - 140, 520]], GREEN, 0.2); poly([[picX + 140, 520], [W, 520]], GREEN, 0.2);
|
|
284
|
+
poly(MTN, GREEN, 0.14);
|
|
285
|
+
// the new picture (a cat) in a targeting frame
|
|
286
|
+
box(picX - 110, picY - 110, 220, 220, GREEN, 0.8);
|
|
287
|
+
cat(picX, picY, 70, GREEN, 1);
|
|
288
|
+
REV = 1; text('NEW PICTURE', picX - tw('NEW PICTURE', 4.2) / 2, picY + 126, 4.2, GREEN, 0.9);
|
|
289
|
+
// model core with dials
|
|
290
|
+
trail((tt, ga) => core(coreX, coreY, 85, WHITE, ga, tt > 8.5 && tt < 9.4 ? 0.22 * (1 - (tt - 8.5) / 0.9) : 0, 1, tt));
|
|
291
|
+
// GUESS beam from picture into model and out to readout
|
|
292
|
+
const g1 = ph(6.8, 0.45);
|
|
293
|
+
REV = g1; arrow(picX + 125, picY, coreX - 120, coreY, GREEN, 0.9); REV = 1;
|
|
294
|
+
const g2 = t > 9.4 ? ph(9.45, 0.4) : 0;
|
|
295
|
+
if (g2 > 0 && g2 < 1) { const px = H.lerp(picX + 125, coreX - 120, g2), py = H.lerp(picY, coreY, g2); cross(px, py, 6, WHITE, 1); }
|
|
296
|
+
const rx = coreX + 175, ry = coreY - 40;
|
|
297
|
+
if (t > 7.15 && t < 9.4) {
|
|
298
|
+
REV = H.clamp((t - 7.15) / 0.3); text('DOG?', rx, ry, 7, t > 7.5 ? RED : GREEN, 1); REV = 1;
|
|
299
|
+
if (t > 7.5) { // CHECK: wrong!
|
|
300
|
+
const bl = Math.floor((t - 7.5) * 8) % 2 === 0 || t > 8.0;
|
|
301
|
+
if (bl) cross(rx + 90, ry + 22, 46, RED, 1);
|
|
302
|
+
if (bl) brackets(picX - 126, picY - 126, 252, 252, 26, RED, 1);
|
|
303
|
+
REV = ph(7.6, 0.4); text('IT IS A CAT', picX - tw('IT IS A CAT', 5) / 2, picY - 160, 5, RED, 1); REV = 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ADJUST: error pulse travels back, dials turn, connections re-shape
|
|
307
|
+
if (t > 8.3) {
|
|
308
|
+
const ek = H.clamp((t - 8.35) / 0.55);
|
|
309
|
+
const path = [[rx + 40, ry + 60], [rx + 40, coreY + 150], [coreX, coreY + 150]];
|
|
310
|
+
REV = ek; poly(path, RED, 0.9); REV = 1;
|
|
311
|
+
if (ek > 0 && ek < 1) { const p = cut(path, ek); const [ex, ey] = p[p.length - 1]; box(ex - 8, ey - 8, 16, 16, RED, 1); }
|
|
312
|
+
if (t > 8.4 && t < 9.4) { REV = ph(8.4, 0.3); text('ERROR', rx + 54, coreY + 124, 5, RED, 1); REV = 1; }
|
|
313
|
+
}
|
|
314
|
+
// weight dials under the core (knobs)
|
|
315
|
+
['W1', 'W2', 'W3'].forEach((s, i) => {
|
|
316
|
+
const dx = coreX - 150 + i * 150, dy = 600, R = 38;
|
|
317
|
+
const circ = []; for (let a = 0; a <= 6.3; a += 0.3) circ.push([dx + Math.cos(a) * R, dy + Math.sin(a) * R]);
|
|
318
|
+
poly(circ, GREEN, 0.7);
|
|
319
|
+
const a0 = [-2.2, -0.6, -1.4][i], a1 = [-0.9, -1.9, 0.2][i];
|
|
320
|
+
trail((tt, ga) => {
|
|
321
|
+
const ang = H.lerp(a0, a1, H.ease.inOut(H.clamp((tt - 8.6 - i * 0.12) / 0.6)));
|
|
322
|
+
poly([[dx, dy], [dx + Math.cos(ang) * (R - 6), dy + Math.sin(ang) * (R - 6)]], t > 8.6 && t < 9.5 ? WHITE : GREEN, ga);
|
|
323
|
+
});
|
|
324
|
+
ctext(s, dx, dy + R + 10, 4, GREEN, 0.9);
|
|
325
|
+
});
|
|
326
|
+
REV = 1; text('WEIGHTS', coreX - 360, 588, 4.2, GREEN, 0.8);
|
|
327
|
+
// second GUESS: correct
|
|
328
|
+
if (t > 9.75) {
|
|
329
|
+
REV = H.clamp((t - 9.75) / 0.3); text('CAT', rx, ry, 7, GREEN, 1); REV = 1;
|
|
330
|
+
if (t > 10.0) { check(rx + 150, ry + 22, 30, GREEN, 1); brackets(picX - 126, picY - 126, 252, 252, 26, GREEN, 1); }
|
|
331
|
+
}
|
|
332
|
+
ZOOM = 1;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ---------- BEAT 4: PROGRESS chart ----------
|
|
336
|
+
if (t >= 10.85) {
|
|
337
|
+
ZOOM = t < 11.2 ? H.ease.out((t - 10.85) / 0.35) : 1;
|
|
338
|
+
const ox = 110, oy = 660, aw = 780, ah = 400;
|
|
339
|
+
REV = ph(10.9, 0.4); poly([[ox, oy - ah], [ox, oy], [ox + aw, oy]], GREEN, 0.8); REV = 1;
|
|
340
|
+
for (let k = 1; k <= 4; k++) poly([[ox - 8, oy - (k / 4) * ah], [ox, oy - (k / 4) * ah]], GREEN, 0.6);
|
|
341
|
+
REV = ph(11.0, 0.4); text('TRAINING ROUNDS', ox + aw - tw('TRAINING ROUNDS', 4.2), oy + 8, 4.2, GREEN, 0.9); REV = 1;
|
|
342
|
+
const N = 24, prog = H.clamp((t - 11.1) / 1.7);
|
|
343
|
+
const acc = (i) => 97 - 36 * Math.exp(-i / 5.5);
|
|
344
|
+
const pts = []; for (let i = 0; i <= N * prog + 1e-6; i++) pts.push([ox + (i / N) * aw, oy - ((acc(i) - 50) / 50) * ah]);
|
|
345
|
+
if (prog * N % 1 > 0.01 && pts.length <= N) { const i = N * prog, i0 = Math.floor(i); pts.push([ox + (i / N) * aw, oy - ((H.lerp(acc(i0), acc(i0 + 1), i - i0) - 50) / 50) * ah]); }
|
|
346
|
+
poly(pts, WHITE, 1);
|
|
347
|
+
pts.forEach(([x, y], k) => { if (k % 4 === 0) box(x - 3, y - 3, 6, 6, GREEN, 0.8); });
|
|
348
|
+
const val = Math.round(61 + 36 * H.ease.out(prog));
|
|
349
|
+
const last = pts[pts.length - 1] || [ox, oy];
|
|
350
|
+
text('ACCURACY', 950, 330, 5, GREEN, 0.95);
|
|
351
|
+
text(val + '%', 950, 376, 15, prog >= 1 ? WHITE : GREEN, 1, 1.3);
|
|
352
|
+
// emphasis: lock-on brackets + ring at the 97% point
|
|
353
|
+
if (prog >= 1) {
|
|
354
|
+
const bl = t > 13.0 || Math.floor((t - 12.8) * 8) % 2 === 0;
|
|
355
|
+
if (bl) brackets(930, 310, 330, 170, 24, RED, 1);
|
|
356
|
+
const ring = []; const rr = 26 + Math.max(0, 0.4 - (t - 12.8)) * 80; for (let a = 0; a <= 6.3; a += 0.25) ring.push([last[0] + Math.cos(a) * rr, last[1] + Math.sin(a) * rr]); poly(ring, RED, 1);
|
|
357
|
+
}
|
|
358
|
+
// ---------- BEAT 5: takeaway ----------
|
|
359
|
+
if (t > 13.1) {
|
|
360
|
+
REV = H.clamp((t - 13.1) / 0.55);
|
|
361
|
+
ctext('MORE EXAMPLES ~ BETTER GUESSES', cx, 132, 7, WHITE, 1, 1.4);
|
|
362
|
+
REV = 1;
|
|
363
|
+
}
|
|
364
|
+
ZOOM = 1;
|
|
365
|
+
}
|
|
366
|
+
exBeams(ctx, W, H_, strokes, r, flick);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ================= SOUND: a 1979-83 vector cabinet. Discrete analog circuits, no music: a low buzzing beam while
|
|
370
|
+
// strokes draw, the two-note heartbeat thump that speeds up with the counter, laser pews, a hyperspace sweep for the
|
|
371
|
+
// warps, a noise-circuit explosion and alarm warble for the miss, and saw-chord bonus tones for the hits.
|
|
372
|
+
// Cabinet fan + mains hum underneath. Times copied from vecExplainer.
|
|
373
|
+
function vecSound(k) {
|
|
374
|
+
const END = 14.5;
|
|
375
|
+
const px = (x) => H.clamp(x / 1280) * 1.4 - 0.7;
|
|
376
|
+
// vector beam drawing: buzzy low saw through a band, pitch drifts from f0 to f1 over the draw
|
|
377
|
+
const beam = (t, dur, f0, f1, p0 = 0, g = 1) => k.tone(t, { f: [[0, f0], [dur, f1]], type: 'sawtooth', dur, gain: 0.05 * g, a: Math.min(0.04, dur * 0.2), hold: dur * 0.7, filter: 'bandpass', f2: 900, q: 1.5, pan: p0, minor: true });
|
|
378
|
+
const saw = (t, f, dur, gain, o) => k.tone(t, Object.assign({ f, type: 'sawtooth', dur, gain, a: 0.003, hold: dur * 0.5, filter: 'lowpass', f2: 2000 }, o));
|
|
379
|
+
// heartbeat thump (alternating low notes)
|
|
380
|
+
const beat = (t, hi, g = 1) => { k.tone(t, { f: [[0, hi ? 70 : 62], [0.09, 40]], type: 'square', dur: 0.12, gain: 0.085 * g, a: 0.002, hold: 0.04, filter: 'lowpass', f2: 300, minor: true }); };
|
|
381
|
+
// laser pew: fast falling square sweep
|
|
382
|
+
const pew = (t, dur, f0, f1, p = 0, g = 1) => k.tone(t, { f: [[0, f0], [dur, f1]], type: 'square', dur, gain: 0.06 * g, a: 0.002, hold: dur * 0.6, filter: 'lowpass', f2: 2200, pan: p });
|
|
383
|
+
// noise-circuit explosion
|
|
384
|
+
const boom = (t, dur, g = 1, p = 0) => { k.noise(t, { dur, color: 'brown', filter: 'lowpass', f: [[0, 1600], [dur, 120]], gain: 0.55 * g, a: 0.002, pan: p }); k.noise(t, { dur: dur * 0.5, filter: 'lowpass', f: 3000, gain: 0.12 * g, rate: 0.3, a: 0.001, pan: p }); };
|
|
385
|
+
// hyperspace: rising (in) or falling (out) noise + saw sweep
|
|
386
|
+
const warp = (t, dur, up, g = 1) => {
|
|
387
|
+
k.noise(t, { dur, color: 'pink', filter: 'bandpass', f: up ? [[0, 300], [dur, 3000]] : [[0, 3000], [dur, 300]], q: 1.4, gain: 0.16 * g, a: up ? dur * 0.85 : 0.01 });
|
|
388
|
+
k.tone(t, { f: up ? [[0, 90], [dur, 700]] : [[0, 700], [dur, 90]], type: 'sawtooth', dur, gain: 0.05 * g, a: up ? dur * 0.8 : 0.01, filter: 'lowpass', f2: 1500 });
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
k.bed('hum', 0, END, { gain: 0.5 });
|
|
392
|
+
k.bed('room', 0, END, { gain: 0.9 }); // cabinet fan
|
|
393
|
+
|
|
394
|
+
// ---- BEAT 1: title draws letter by letter (0.15-1.05), horizon line (0.9-1.4)
|
|
395
|
+
beam(0.15, 0.9, 120, 150, 0);
|
|
396
|
+
for (let i = 0; i < 18; i += 2) k.noise(0.15 + (i / 18) * 0.9, { dur: 0.012, filter: 'bandpass', f: 1300, q: 3, gain: 0.12, a: 0.001, pan: px(440 + i * 22), minor: true });
|
|
397
|
+
k.tone(0.9, { f: [[0, 150], [0.5, 260]], type: 'sawtooth', dur: 0.5, gain: 0.04, a: 0.05, hold: 0.35, filter: 'lowpass', f2: 800, pan: 0 });
|
|
398
|
+
// title shrinks into the corner (1.7-2.3): soft low slide, small tone as it turns red (~2.0)
|
|
399
|
+
k.noise(1.7, { dur: 0.6, color: 'brown', filter: 'lowpass', f: [[0, 600], [0.6, 200]], gain: 0.09, a: 0.25, pan: -0.4 });
|
|
400
|
+
saw(2.0, 'E3', 0.12, 0.035, { pan: -0.6 });
|
|
401
|
+
// ---- BEAT 2: core draws edge by edge (1.95-2.95), stem + MODEL (2.4/2.6), arrow (2.6-3.1), label (2.9)
|
|
402
|
+
beam(1.95, 1.0, 80, 160, px(820), 1.1);
|
|
403
|
+
beam(2.6, 0.5, 140, 200, px(450), 0.7);
|
|
404
|
+
// cards fly into the core: arrive at 2.6 + i*0.34 + 1.5 (only the ones before the warp)
|
|
405
|
+
for (let i = 0; i < 6; i++) { const t = 4.1 + i * 0.34; if (t > 5.75) break; k.tone(t, { f: [[0, 220], [0.08, 520]], type: 'square', dur: 0.1, gain: 0.04, a: 0.002, hold: 0.05, filter: 'lowpass', f2: 1600, pan: px(780), minor: true }); }
|
|
406
|
+
// EXAMPLES SEEN (box 2.8, counter 2.9-5.5, accelerating): the heartbeat speeds up with it
|
|
407
|
+
{ let hi = true; for (let t = 2.95; t < 5.45;) { beat(t, hi); hi = !hi; t += H.lerp(0.42, 0.13, H.clamp((t - 2.9) / 2.6)); } }
|
|
408
|
+
saw(5.5, 'A3', 0.1, 0.05, { pan: -0.4 }); saw(5.6, 'E4', 0.3, 0.05, { pan: -0.4 }); // lands on 1,000,000
|
|
409
|
+
// warp into the core (5.8-6.4), out of the streaks into the loop (6.4-6.75)
|
|
410
|
+
warp(5.8, 0.6, true, 1);
|
|
411
|
+
k.play('thud', 6.4, { f: 90, gain: 0.45 });
|
|
412
|
+
warp(6.4, 0.35, false, 0.6);
|
|
413
|
+
|
|
414
|
+
// ---- BEAT 3: guess beam (6.8-7.25), DOG? draws (7.15-7.45)
|
|
415
|
+
pew(6.8, 0.45, 1400, 500, px(600), 0.9);
|
|
416
|
+
beam(7.15, 0.3, 130, 150, px(1100), 0.8);
|
|
417
|
+
// CHECK (7.5): red cross + brackets blink (on at 7.5, 7.75, steady from 8.0) -> explosion + alarm warble
|
|
418
|
+
boom(7.5, 0.7, 1.5, px(1080)); k.play('thud', 7.5, { f: 80, gain: 0.5, pan: px(1080) });
|
|
419
|
+
[7.5, 7.75, 8.0].forEach((t) => { saw(t, 'C4', 0.06, 0.06, { pan: px(260) }); saw(t + 0.06, 'G3', 0.06, 0.06, { pan: px(260) }); });
|
|
420
|
+
beam(7.6, 0.4, 120, 110, px(260), 0.7); // IT IS A CAT
|
|
421
|
+
// ADJUST (8.4): error pulse runs back (8.35-8.9), core wobbles 8.5-9.4, dials turn (8.6, 8.72, 8.84 for 0.6)
|
|
422
|
+
k.tone(8.35, { f: [[0, 600], [0.55, 200]], type: 'square', dur: 0.56, gain: 0.04, a: 0.01, hold: 0.4, filter: 'lowpass', f2: 1500, pan: px(1000) });
|
|
423
|
+
k.withMinor(() => {
|
|
424
|
+
for (let i = 0; i < 3; i++) k.tone(8.6 + i * 0.12, { f: [[0, 160 + i * 30], [0.6, 230 + i * 30]], type: 'sawtooth', dur: 0.6, gain: 0.03, a: 0.08, hold: 0.3, filter: 'lowpass', f2: 700, pan: px(690 + i * 150) });
|
|
425
|
+
for (let t = 8.5; t < 9.35; t += 1 / 9) k.tone(t, { f: 95 + 30 * Math.sin(t * 9), type: 'sawtooth', dur: 0.1, gain: 0.04 * (1 - (t - 8.5) / 0.9), filter: 'lowpass', f2: 500, pan: px(840) });
|
|
426
|
+
});
|
|
427
|
+
// GUESS again: a shot travels from picture to core (9.45-9.85), CAT draws (9.75), lock-on check (10.0): the correction
|
|
428
|
+
pew(9.45, 0.4, 1200, 400, px(560), 1.0);
|
|
429
|
+
beam(9.75, 0.3, 140, 170, px(1100), 0.8);
|
|
430
|
+
saw(10.0, 'E4', 0.1, 0.06, { pan: px(1100) }); saw(10.1, 'A4', 0.35, 0.06, { pan: px(1100) }); saw(10.1, 'A3', 0.35, 0.05, { pan: px(1100) });
|
|
431
|
+
// zoom out of the loop (10.5-10.9), chart zooms in (10.85-11.2)
|
|
432
|
+
warp(10.5, 0.4, false, 0.7);
|
|
433
|
+
|
|
434
|
+
// ---- BEAT 4: axes draw (10.9-11.3), curve draws 11.1-12.8 with the % climbing (ease-out)
|
|
435
|
+
beam(10.9, 0.4, 110, 130, px(400), 0.8);
|
|
436
|
+
k.tone(11.1, { f: [[0, 120], [0.6, 210], [1.7, 240]], type: 'sawtooth', dur: 1.7, gain: 0.045, a: 0.05, hold: 1.5, filter: 'bandpass', f2: 900, q: 1.5, pan: px(500), minor: true });
|
|
437
|
+
{ let last = 61, lastT = -1; for (let f = Math.ceil(11.1 * 60); f < 12.75 * 60; f++) { const t = f / 60, v = Math.round(61 + 36 * H.ease.out(H.clamp((t - 11.1) / 1.7))); if (v > last && t - lastT >= 1 / 12) { k.noise(t, { dur: 0.014, filter: 'bandpass', f: 1600, q: 3, gain: 0.13, a: 0.001, pan: px(1080), minor: true }); last = v; lastT = t; } } }
|
|
438
|
+
// 97% (12.8): lock-on brackets blink, ring collapses on the last point. the biggest hit: deep boom + saw chord
|
|
439
|
+
const P = 12.8;
|
|
440
|
+
k.play('thud', P, { f: 110, gain: 0.8 }); boom(P, 0.5, 0.6, px(1000));
|
|
441
|
+
['C4', 'E4', 'G4', 'C5'].forEach((n) => saw(P, n, 0.9, 0.04, { hold: 0.35, verb: 0.15, pan: px(1080) })); k.tone(P, { f: 'C2', dur: 0.9, gain: 0.25, a: 0.004, hold: 0.2 });
|
|
442
|
+
k.tone(P, { f: [[0, 900], [0.4, 300]], type: 'square', dur: 0.4, gain: 0.03, a: 0.005, hold: 0.25, filter: 'lowpass', f2: 1600, pan: px(870) }); // ring shrinking
|
|
443
|
+
// ---- BEAT 5: takeaway draws (13.1-13.65) and lands with a resolving two-note
|
|
444
|
+
beam(13.1, 0.55, 140, 170, 0, 0.9);
|
|
445
|
+
saw(13.68, 'G3', 0.12, 0.04); saw(13.8, 'C4', 0.7, 0.04, { verb: 0.12 }); k.tone(13.8, { f: 'C3', type: 'triangle', dur: 0.8, gain: 0.09, a: 0.005, hold: 0.3 });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
registerStyle({
|
|
449
|
+
id: 'vector-arcade',
|
|
450
|
+
name: 'Vector Arcade 1980',
|
|
451
|
+
category: 'Retro Digital',
|
|
452
|
+
description: 'An XY vector monitor cabinet game: hair-thin phosphor beams on pure black, additive bloom, brighter dwell dots where the beam pauses at vertices, dimmer long strokes, wireframe 3D in perspective and a hand-built stroke font.',
|
|
453
|
+
motion: 'Attract-mode build: the beam traces the horizon and mountains, wireframes draw on edge by edge, the AI core flies in from the distance spinning, the saucer slides in with phosphor trails, then the radar, score and title are stroked in letter by letter while everything flickers with beam load.',
|
|
454
|
+
duration: 5,
|
|
455
|
+
frameT: 4.8,
|
|
456
|
+
readsPixels: false,
|
|
457
|
+
explainerDuration: 14.5,
|
|
458
|
+
explainerNote: 'Told as a vector-cabinet HUD: the question shrinks into a red corner readout, labelled wireframe cards stream into a spinning icosahedron core, a hyperspace-style zoom carries us into a lock-on loop (GUESS > CHECK > ADJUST with brackets, red error beam, turning weight dials), and the stroked accuracy graph climbs to a flashing 97%.',
|
|
459
|
+
explainer(ctx, W, H_, t) { vecExplainer(ctx, W, H_, t); },
|
|
460
|
+
explainerSound(k) { vecSound(k); },
|
|
461
|
+
draw(ctx, W, H_, t) {
|
|
462
|
+
ctx.fillStyle = '#000'; ctx.fillRect(0, 0, W, H_);
|
|
463
|
+
const r = H.rng(1980 + Math.floor(t * 30)); // per-frame beam jitter/flicker
|
|
464
|
+
const rs = H.rng(42); // static scene randomness
|
|
465
|
+
const GREEN = [110, 255, 120], RED = [255, 70, 50], WHITE = [210, 255, 220];
|
|
466
|
+
const globalFlick = 0.93 + 0.07 * r();
|
|
467
|
+
|
|
468
|
+
// collect beam strokes first, render them in bloom passes after
|
|
469
|
+
const strokes = []; // {pts:[[x,y]...], col, a}
|
|
470
|
+
let REV = 1; // fraction of each path the beam has traced so far
|
|
471
|
+
const cut = (pts, f) => {
|
|
472
|
+
if (f >= 1) return pts; if (f <= 0) return [];
|
|
473
|
+
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]);
|
|
474
|
+
let left = L * f; const out = [pts[0]];
|
|
475
|
+
for (let i = 1; i < pts.length; i++) {
|
|
476
|
+
const d = Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]);
|
|
477
|
+
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; }
|
|
478
|
+
out.push(pts[i]); left -= d;
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
};
|
|
482
|
+
let GHOST = false;
|
|
483
|
+
const poly = (pts, col = GREEN, a = 1) => { pts = cut(pts, REV); if (pts.length > 1) strokes.push({ pts, col, a, ghost: GHOST }); };
|
|
484
|
+
const ph = (a, d) => H.ease.inOut(H.clamp((t - a) / d));
|
|
485
|
+
|
|
486
|
+
// ---------- 3D camera ----------
|
|
487
|
+
const camH = 1.5, f = 760, hz = 420, cx = W / 2;
|
|
488
|
+
const yaw = Math.sin(t * 0.25) * 0.08;
|
|
489
|
+
const proj = ([x, y, z]) => {
|
|
490
|
+
const xr = x * Math.cos(yaw) - z * Math.sin(yaw), zr = x * Math.sin(yaw) + z * Math.cos(yaw);
|
|
491
|
+
if (zr < 0.5) return null;
|
|
492
|
+
return [cx + (f * xr) / zr, hz - (f * (y - camH)) / zr, zr];
|
|
493
|
+
};
|
|
494
|
+
const edges3 = (verts, edges, col, a = 1) => {
|
|
495
|
+
const P = verts.map(proj);
|
|
496
|
+
const rev0 = REV, k = REV * edges.length;
|
|
497
|
+
edges.forEach(([i, j], e) => { REV = H.clamp(k - e); if (P[i] && P[j] && REV > 0) poly([[P[i][0], P[i][1]], [P[j][0], P[j][1]]], col, a * H.clamp(2.2 - P[i][2] / 30, 0.35, 1)); });
|
|
498
|
+
REV = rev0;
|
|
499
|
+
};
|
|
500
|
+
const rotY = (v, a) => [v[0] * Math.cos(a) - v[2] * Math.sin(a), v[1], v[0] * Math.sin(a) + v[2] * Math.cos(a)];
|
|
501
|
+
const rotX = (v, a) => [v[0], v[1] * Math.cos(a) - v[2] * Math.sin(a), v[1] * Math.sin(a) + v[2] * Math.cos(a)];
|
|
502
|
+
const tr = (v, p) => [v[0] + p[0], v[1] + p[1], v[2] + p[2]];
|
|
503
|
+
|
|
504
|
+
// ---------- horizon + mountain range + moon + volcano ----------
|
|
505
|
+
REV = ph(0.1, 0.35); poly([[0, hz], [W, hz]], GREEN, 0.75);
|
|
506
|
+
REV = ph(0.35, 0.6);
|
|
507
|
+
const mx = (x) => (((x - t * 18 * 0 - yaw * 900) % (W + 400)) + W + 400) % (W + 400) - 200;
|
|
508
|
+
const mtn = [];
|
|
509
|
+
for (let i = 0; i <= 26; i++) { const x = -200 + i * ((W + 400) / 26); const h = (i % 2 ? 10 + rs() * 30 : 40 + rs() * 70); mtn.push([x - yaw * 900, hz - h * (i > 9 && i < 15 ? 0.45 : 1)]); }
|
|
510
|
+
poly(mtn, GREEN, 0.7);
|
|
511
|
+
REV = ph(0.6, 0.3);
|
|
512
|
+
// volcano (left) with erupting debris arcs
|
|
513
|
+
const vx = 1170 - yaw * 900;
|
|
514
|
+
poly([[vx - 90, hz], [vx - 22, hz - 92], [vx + 22, hz - 92], [vx + 90, hz]], GREEN, 0.75);
|
|
515
|
+
for (let k = 0; k < (t > 0.9 ? 6 : 0); k++) {
|
|
516
|
+
const ph = ((t * 0.7 + k / 6) % 1), dir = (k % 2 ? 1 : -1) * (0.3 + rs() * 0.7);
|
|
517
|
+
const px = vx + dir * ph * 90, py = hz - 92 - (ph * 160 - ph * ph * 190);
|
|
518
|
+
poly([[px, py], [px + dir * 6, py - 4]], GREEN, 1 - ph * 0.6);
|
|
519
|
+
}
|
|
520
|
+
// crescent moon (right)
|
|
521
|
+
const moon = [];
|
|
522
|
+
for (let a = -1.9; a <= 1.9; a += 0.12) moon.push([1040 + Math.cos(a) * 34 - yaw * 900, 235 + Math.sin(a) * 34]);
|
|
523
|
+
for (let a = 1.6; a >= -1.6; a -= 0.12) moon.push([1054 + Math.cos(a) * 26 - yaw * 900, 235 + Math.sin(a) * 30]);
|
|
524
|
+
REV = ph(0.7, 0.4); poly(moon, GREEN, 0.8);
|
|
525
|
+
|
|
526
|
+
// ---------- ground objects: pyramids + blocks in perspective ----------
|
|
527
|
+
const pyramid = (p, s, h) => edges3(
|
|
528
|
+
[[-s, 0, -s], [s, 0, -s], [s, 0, s], [-s, 0, s], [0, h, 0]].map((v) => tr(v, p)),
|
|
529
|
+
[[0, 1], [1, 2], [2, 3], [3, 0], [0, 4], [1, 4], [2, 4], [3, 4]], GREEN);
|
|
530
|
+
const block = (p, sx, sy, sz) => edges3(
|
|
531
|
+
[[-sx, 0, -sz], [sx, 0, -sz], [sx, 0, sz], [-sx, 0, sz], [-sx, sy, -sz], [sx, sy, -sz], [sx, sy, sz], [-sx, sy, sz]].map((v) => tr(v, p)),
|
|
532
|
+
[[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]], GREEN);
|
|
533
|
+
let oi = 0; const nx = () => { REV = H.clamp((t - 0.8 - (oi++) * 0.1) / 0.3); };
|
|
534
|
+
nx(); pyramid([-12, 0, 27], 2, 3.4); nx(); block([11.5, 0, 19], 1.6, 2.2, 1.6); nx(); pyramid([9.5, 0, 30], 2.4, 4); nx(); block([-17, 0, 15], 1.2, 1.2, 1.2);
|
|
535
|
+
nx(); pyramid([-30, 0, 44], 3, 5); nx(); block([-6, 0, 36], 2.2, 1.4, 1.4); nx(); pyramid([24, 0, 58], 3, 5);
|
|
536
|
+
|
|
537
|
+
// ---------- AI CORE: rotating icosahedron + counter-rotating inner cube (focal point) ----------
|
|
538
|
+
const phi = (1 + Math.sqrt(5)) / 2;
|
|
539
|
+
const icoV = [[-1, phi, 0], [1, phi, 0], [-1, -phi, 0], [1, -phi, 0], [0, -1, phi], [0, 1, phi], [0, -1, -phi], [0, 1, -phi], [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1], [-phi, 0, 1]];
|
|
540
|
+
const icoE = [];
|
|
541
|
+
for (let i = 0; i < 12; i++) for (let j = i + 1; j < 12; j++) { const d = Math.hypot(icoV[i][0] - icoV[j][0], icoV[i][1] - icoV[j][1], icoV[i][2] - icoV[j][2]); if (Math.abs(d - 2) < 0.01) icoE.push([i, j]); }
|
|
542
|
+
const corePAt = (tt) => [0.4, 3.3 + Math.sin(tt * 1.3) * 0.2, 15 + 50 * (1 - H.ease.out(H.clamp((tt - 1.2) / 1.3)))];
|
|
543
|
+
const coreP = corePAt(t);
|
|
544
|
+
REV = H.clamp((t - 1.2) / 0.6);
|
|
545
|
+
const ca = t * 0.6, cb = t * 0.37;
|
|
546
|
+
const cubeV = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]];
|
|
547
|
+
// phosphor persistence: the spinning core leaves a fading smear of copies over the last ~50 ms
|
|
548
|
+
[[0.048, 0.07], [0.036, 0.12], [0.024, 0.2], [0.012, 0.32], [0, 1]].forEach(([lag, ga]) => {
|
|
549
|
+
const tt = t - lag, pa = tt * 0.6, pb = tt * 0.37, cp = corePAt(tt); GHOST = lag > 0;
|
|
550
|
+
edges3(icoV.map((v) => tr(rotX(rotY(v.map((q) => q * 1.05), pa), pb), cp)), icoE, WHITE, ga);
|
|
551
|
+
edges3(cubeV.map((v) => tr(rotY(rotX(v.map((q) => q * 0.62), -pa * 1.7), -pb * 2), cp)), [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]], GREEN, ga);
|
|
552
|
+
});
|
|
553
|
+
GHOST = false;
|
|
554
|
+
// orbit ring around core
|
|
555
|
+
const ring = []; for (let a = 0; a <= 6.3; a += 0.15) { const v = proj(tr(rotX([Math.cos(a + t) * 2.6, 0, Math.sin(a + t) * 2.6], 0.3), coreP)); if (v) ring.push([v[0], v[1]]); }
|
|
556
|
+
REV = ph(2.1, 0.5); poly(ring, GREEN, 0.55);
|
|
557
|
+
REV = 1;
|
|
558
|
+
|
|
559
|
+
// ---------- enemy saucer with phosphor-decay trail ----------
|
|
560
|
+
const saucerAt = (tt) => [-3 + Math.sin(tt * 0.9) * 1.6 - 16 * (1 - H.ease.out(H.clamp((tt - 1.7) / 1.1))), 1.0, 12 + Math.cos(tt * 0.9) * 2];
|
|
561
|
+
if (t > 1.7) [0.12, 0.06, 0].forEach((lag, li) => {
|
|
562
|
+
const p = saucerAt(t - lag), sa = (t - lag) * 2.5;
|
|
563
|
+
const ring1 = [], ring2 = [], spokes = [];
|
|
564
|
+
const n = 8;
|
|
565
|
+
const v1 = [], v2 = [], v3 = [];
|
|
566
|
+
for (let k = 0; k < n; k++) { const a = sa + (k / n) * Math.PI * 2; v1.push([Math.cos(a) * 1.3, 0, Math.sin(a) * 1.3]); v2.push([Math.cos(a) * 0.55, 0.45, Math.sin(a) * 0.55]); v3.push([Math.cos(a) * 0.6, -0.25, Math.sin(a) * 0.6]); }
|
|
567
|
+
const V = [...v1, ...v2, ...v3].map((v) => tr(v, p));
|
|
568
|
+
const E = [];
|
|
569
|
+
for (let k = 0; k < n; k++) { E.push([k, (k + 1) % n], [n + k, n + (k + 1) % n], [2 * n + k, 2 * n + (k + 1) % n], [k, n + k]); if (k % 2 === 0) E.push([k, 2 * n + k]); }
|
|
570
|
+
edges3(V, E, GREEN, [0.08, 0.16, 0.8][li]);
|
|
571
|
+
});
|
|
572
|
+
// player shell flying toward the core
|
|
573
|
+
const shellK = (t * 0.9) % 1;
|
|
574
|
+
if (t > 2.6) [0.08, 0.04, 0].forEach((lag, li) => { const k = ((t - lag - 2.6) * 0.9) % 1; const p = proj([0.2, 0.6 + k * 3.2, 2 + k * 10]); if (p) poly([[p[0] - 3, p[1]], [p[0] + 3, p[1]], [p[0], p[1] - 5], [p[0] - 3, p[1]]], GREEN, [0.2, 0.4, 1][li]); });
|
|
575
|
+
|
|
576
|
+
// ---------- gunsight reticle ----------
|
|
577
|
+
const ry = 520;
|
|
578
|
+
REV = ph(1.6, 0.35);
|
|
579
|
+
poly([[cx, ry - 120], [cx, ry - 60]], GREEN, 0.9); poly([[cx, ry + 20], [cx, ry + 80]], GREEN, 0.9);
|
|
580
|
+
poly([[cx - 70, ry - 40], [cx - 70, ry - 60], [cx - 20, ry - 60]], GREEN, 0.9); poly([[cx + 70, ry - 40], [cx + 70, ry - 60], [cx + 20, ry - 60]], GREEN, 0.9);
|
|
581
|
+
poly([[cx - 70, ry], [cx - 70, ry + 20], [cx - 20, ry + 20]], GREEN, 0.9); poly([[cx + 70, ry], [cx + 70, ry + 20], [cx + 20, ry + 20]], GREEN, 0.9);
|
|
582
|
+
|
|
583
|
+
// ---------- HUD (red overlay zone, like the cabinet's colour gel) ----------
|
|
584
|
+
const text = (str, x, y, sz, col, a = 1, track = 1.6) => {
|
|
585
|
+
let px = x;
|
|
586
|
+
const nC = REV * str.length, rev0 = REV;
|
|
587
|
+
for (let ci = 0; ci < str.length; ci++) {
|
|
588
|
+
const ch = str[ci]; REV = H.clamp(nC - ci);
|
|
589
|
+
const g = GLYPH[ch];
|
|
590
|
+
if (g) g.forEach((pl) => poly(pl.map(([gx, gy]) => [px + gx * sz, y + gy * sz]), col, a));
|
|
591
|
+
px += sz * 4 + sz * track;
|
|
592
|
+
}
|
|
593
|
+
REV = rev0;
|
|
594
|
+
return px - x;
|
|
595
|
+
};
|
|
596
|
+
const textW = (str, sz, track = 1.6) => str.length * (sz * 4 + sz * track) - sz * track;
|
|
597
|
+
// radar
|
|
598
|
+
const rx0 = cx, ry0 = 92, rr = 62;
|
|
599
|
+
REV = ph(1.9, 0.45);
|
|
600
|
+
const circ = []; for (let a = 0; a <= 6.30; a += 0.2) circ.push([rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]); poly(circ, RED, 0.85);
|
|
601
|
+
for (let k = 0; k < 8; k++) { const a = k * Math.PI / 4; poly([[rx0 + Math.cos(a) * (rr - 8), ry0 + Math.sin(a) * (rr - 8)], [rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]], RED, 0.7); }
|
|
602
|
+
const sw = t * 2.2;
|
|
603
|
+
REV = t > 2.35 ? 1 : 0;
|
|
604
|
+
poly([[rx0, ry0], [rx0 + Math.cos(sw) * rr, ry0 + Math.sin(sw) * rr]], RED, 1);
|
|
605
|
+
if (REV > 0) { GHOST = true; for (let k = 1; k <= 5; k++) { const a = sw - k * 0.07; poly([[rx0, ry0], [rx0 + Math.cos(a) * rr, ry0 + Math.sin(a) * rr]], RED, 0.5 * Math.pow(0.6, k)); } GHOST = false; }
|
|
606
|
+
[[0.5, 0.6], [2.4, 0.4], [4.1, 0.75]].forEach(([a, d]) => { const fade = ((sw - a) % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2); const al = H.clamp(1 - fade / 4); if (al > 0.05) { const bx = rx0 + Math.cos(a) * rr * d, by = ry0 + Math.sin(a) * rr * d; poly([[bx - 2, by], [bx + 2, by]], RED, al); } });
|
|
607
|
+
poly([[rx0 - 6, ry0 - 14], [rx0, ry0 - 22], [rx0 + 6, ry0 - 14]], RED, 0.8);
|
|
608
|
+
// score / hi-score
|
|
609
|
+
REV = H.clamp((t - 2.2) / 0.3); text('SCORE', 60, 40, 4, RED, 0.85);
|
|
610
|
+
REV = H.clamp((t - 2.35) / 0.35); text(String(42150 + Math.floor(Math.max(0, t - 2.4) * 50) * 10).padStart(7, '0'), 60, 74, 6, GREEN, 1);
|
|
611
|
+
const hs = 'HI 0099900'; REV = H.clamp((t - 2.3) / 0.35); text(hs, W - 60 - textW(hs, 4), 40, 4, RED, 0.85);
|
|
612
|
+
REV = t > 2.6 ? 1 : 0;
|
|
613
|
+
// lives as tiny tank glyphs
|
|
614
|
+
for (let k = 0; k < 3; k++) { const lx = W - 60 - textW(hs, 4) + k * 34, ly = 82; poly([[lx, ly + 12], [lx + 24, ly + 12], [lx + 20, ly + 6], [lx + 4, ly + 6], [lx, ly + 12]], GREEN, 0.9); poly([[lx + 8, ly + 6], [lx + 10, ly + 1], [lx + 16, ly + 1], [lx + 17, ly + 6]], GREEN, 0.9); poly([[lx + 16, ly + 3], [lx + 26, ly + 2]], GREEN, 0.9); }
|
|
615
|
+
// blinking alert line
|
|
616
|
+
REV = 1;
|
|
617
|
+
if (t > 2.9 && Math.floor(t * 2.5) % 3 !== 1) { const msg = 'AI CORE IN RANGE'; text(msg, cx - textW(msg, 5) / 2, 180, 5, RED, 1); }
|
|
618
|
+
// title, bottom
|
|
619
|
+
const title = 'NEURAL ZONE';
|
|
620
|
+
REV = H.clamp((t - 2.4) / 0.7); text(title, cx - textW(title, 11, 1.4) / 2, 632, 11, GREEN, 1, 1.4);
|
|
621
|
+
REV = 1;
|
|
622
|
+
|
|
623
|
+
// ---------- render: beam strokes with intensity model, additive bloom, tube glass ----------
|
|
624
|
+
renderBeams(ctx, W, H_, strokes, r, globalFlick, 0.22);
|
|
625
|
+
},
|
|
626
|
+
});
|
|
627
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 72,
|
|
3
|
+
"name": "Vector Arcade 1980",
|
|
4
|
+
"nameZh": "1980年代矢量街机",
|
|
5
|
+
"family": "Retro Digital",
|
|
6
|
+
"familyZh": "复古数字",
|
|
7
|
+
"description": "An XY vector monitor cabinet game: hair-thin phosphor beams on pure black, additive bloom, brighter dwell dots where the beam pauses at vertices, dimmer long strokes, wireframe 3D in perspective and a hand-built stroke font.",
|
|
8
|
+
"motionNotes": "Motion: Attract-mode build: the beam traces the horizon and mountains, wireframes draw on edge by edge, the AI core flies in from the distance spinning, the saucer slides in with phosphor trails, then the radar, score and title are stroked in letter by letter while everything flickers with beam load.",
|
|
9
|
+
"explainerNotes": "Explainer: Told as a vector-cabinet HUD: the question shrinks into a red corner readout, labelled wireframe cards stream into a spinning icosahedron core, a hyperspace-style zoom carries us into a lock-on loop (GUESS > CHECK > ADJUST with brackets, red error beam, turning weight dials), and the stroked accuracy graph climbs to a flashing 97%.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "vector-arcade",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|