@autoaiai/aicut 0.1.7 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANUAL.md +94 -84
- package/package.json +1 -1
- package/product.json +2 -1
- package/skills/aicut/SKILL.md +4 -4
- package/skills/aicut/references/commands.md +15 -15
- package/skills/aicut/references/film-format.md +1 -1
- package/skills/aicut/references/recipes.md +4 -4
- package/skills/aicut/references/troubleshooting.md +2 -2
- package/skills/aicut-motion/SKILL.md +75 -9
- package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
- package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
- package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
- package/skills/aicut-motion/references/design-systems/README.md +1 -1
- package/skills/aicut-motion/references/motion-delivery.md +55 -0
- package/skills/aicut-motion/references/pages.md +1 -1
- package/skills/aicut-motion/references/style-index.json +690 -0
- package/skills/aicut-motion/references/style-research.md +73 -0
- package/skills/aicut-motion/references/style-selection.md +71 -0
- package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
- package/skills/aicut-motion/scripts/atlas.mjs +109 -0
- package/studio/client.mjs +3 -3
- package/studio/server/fetch-github.mjs +14 -3
- package/studio/server/server.mjs +71 -6
- package/studio/server/skill-summaries.mjs +60 -0
- package/studio/server/skills.mjs +121 -7
- package/studio/server/styles-store.mjs +235 -0
- package/studio/server/template-posters.mjs +2 -2
- package/studio/server/templates-page.mjs +93 -93
- package/studio/server/templates.mjs +32 -7
- package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
- package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
- package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
- package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
- package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
- package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
- package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
- package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
- package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
- package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
- package/studio/ui/dist/index.html +4 -3
- package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
- package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
- package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
- package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
- package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
- package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
- package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
- package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 66,
|
|
3
|
+
"name": "Hidden-Line Wireframe",
|
|
4
|
+
"nameZh": "隐藏线框图",
|
|
5
|
+
"family": "Dimensional",
|
|
6
|
+
"familyZh": "立体空间",
|
|
7
|
+
"description": "Vector-display 3D: a perspective wire-mesh landscape with true hidden-line removal (back-to-front quads filled with the night before stroking), a wireframe moon, phosphor-cyan lines with bloom on deep navy, and HUD coordinate labels.",
|
|
8
|
+
"motionNotes": "Motion: HUD brackets snap on, the wireframe moon traces itself, then the hidden-line mesh is scanned in row by row from the camera to the horizon behind a bright scan front; markers lock on and labels type out while the camera glides forward and the scan keeps sweeping.",
|
|
9
|
+
"explainerNotes": "Explainer: Learning is shown as descent over the hidden-line “loss landscape”: the camera flies the mesh from a data-feed HUD streaming into the wireframe-sphere model, down to an amber “model weights” marker whose error becomes a dashed downhill vector, then follows it into the glowing basin; labels are bracketed HUD tags that type on, numbers are big Space Mono readouts.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "wireframe-terrain",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
{
|
|
2
|
+
const K = '#151515';
|
|
3
|
+
const PAL = { pink: '#f9c3d2', mint: '#a9e5d0', lilac: '#cbb8f4', butter: '#fff0a6', cream: '#fff8ec', magenta: '#ff4f9b', blue: '#2b62ff', yellow: '#ffd02e', teal: '#00b2a0', red: '#ff5a3c' };
|
|
4
|
+
|
|
5
|
+
// filled + outlined + hard drop shadow
|
|
6
|
+
const solid = (c, path, fill, sh = 10, lw = 5) => {
|
|
7
|
+
c.save(); c.translate(sh, sh); c.fillStyle = K; c.fill(path); c.restore();
|
|
8
|
+
c.fillStyle = fill; c.fill(path); c.lineWidth = lw; c.strokeStyle = K; c.lineJoin = 'round'; c.stroke(path);
|
|
9
|
+
};
|
|
10
|
+
const squigglePath = (x, y, len, amp, waves, ang, ph = 0, frac = 1) => {
|
|
11
|
+
const p = new Path2D(), ca = Math.cos(ang), sa = Math.sin(ang);
|
|
12
|
+
for (let i = 0; i <= Math.round(80 * frac); i++) {
|
|
13
|
+
const u = i / 80, lx = u * len, ly = Math.sin(u * waves * Math.PI * 2 + ph) * amp;
|
|
14
|
+
const X = x + lx * ca - ly * sa, Y = y + lx * sa + ly * ca;
|
|
15
|
+
i ? p.lineTo(X, Y) : p.moveTo(X, Y);
|
|
16
|
+
}
|
|
17
|
+
return p;
|
|
18
|
+
};
|
|
19
|
+
const zigzagPath = (x, y, len, amp, n, ang, frac = 1) => {
|
|
20
|
+
const p = new Path2D(), ca = Math.cos(ang), sa = Math.sin(ang), f = frac * n;
|
|
21
|
+
for (let i = 0; i <= Math.ceil(f); i++) { const ii = Math.min(i, f), i0 = Math.floor(ii), fr = ii - i0, ya = i0 % 2 ? -amp : amp, yb = (i0 + 1) % 2 ? -amp : amp; const lx = (ii / n) * len, ly = ya + (yb - ya) * fr; const X = x + lx * ca - ly * sa, Y = y + lx * sa + ly * ca; i ? p.lineTo(X, Y) : p.moveTo(X, Y); }
|
|
22
|
+
return p;
|
|
23
|
+
};
|
|
24
|
+
const strokeOutlined = (c, path, col, w, ow = 4, sh = 8) => {
|
|
25
|
+
c.lineCap = 'round'; c.lineJoin = 'round';
|
|
26
|
+
c.save(); c.translate(sh, sh); c.strokeStyle = K; c.lineWidth = w + ow * 2; c.stroke(path); c.restore();
|
|
27
|
+
c.strokeStyle = K; c.lineWidth = w + ow * 2; c.stroke(path);
|
|
28
|
+
c.strokeStyle = col; c.lineWidth = w; c.stroke(path);
|
|
29
|
+
};
|
|
30
|
+
const terrazzo = (c, path, seed, base = PAL.cream) => {
|
|
31
|
+
c.save(); c.clip(path); c.fillStyle = base; c.fillRect(0, 0, 1280, 720);
|
|
32
|
+
const r = H.rng(seed), cols = [PAL.magenta, PAL.blue, PAL.yellow, PAL.teal, K, PAL.pink, PAL.lilac, PAL.red];
|
|
33
|
+
const bb = { x0: 0, y0: 0, x1: 1280, y1: 720 };
|
|
34
|
+
for (let i = 0; i < 2400; i++) {
|
|
35
|
+
const x = r.range(bb.x0, bb.x1), y = r.range(bb.y0, bb.y1);
|
|
36
|
+
if (!c.isPointInPath(path, x, y)) continue;
|
|
37
|
+
const s = r.range(3, 11) * (r() < 0.08 ? 2 : 1), n = r.int(3, 6), a0 = r.range(0, 7);
|
|
38
|
+
c.fillStyle = r.pick(cols); c.beginPath();
|
|
39
|
+
for (let k = 0; k < n; k++) { const a = a0 + (k / n) * Math.PI * 2 + r.range(-0.3, 0.3), rr = s * r.range(0.5, 1); k ? c.lineTo(x + Math.cos(a) * rr, y + Math.sin(a) * rr) : c.moveTo(x + Math.cos(a) * rr, y + Math.sin(a) * rr); }
|
|
40
|
+
c.closePath(); c.fill();
|
|
41
|
+
}
|
|
42
|
+
c.restore();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ===================== EXPLAINER: "How does AI learn?" =====================
|
|
46
|
+
// Memphis cut-out animation on twos: scenes change with a striped band that swipes across, arrows are fat
|
|
47
|
+
// outlined shapes, counters are stickers that pop, emphasis is a spiky starburst.
|
|
48
|
+
const roundRect = (x, y, w, h, r) => { const p = new Path2D(); p.moveTo(x + r, y); p.arcTo(x + w, y, x + w, y + h, r); p.arcTo(x + w, y + h, x, y + h, r); p.arcTo(x, y + h, x, y, r); p.arcTo(x, y, x + w, y, r); p.closePath(); return p; };
|
|
49
|
+
const bubble = (w, h, tail) => { // tail: [x, y] of the tip relative to centre
|
|
50
|
+
const p = new Path2D(), r = 34, x = -w / 2, y = -h / 2;
|
|
51
|
+
p.moveTo(x + r, y); p.arcTo(x + w, y, x + w, y + h, r); p.arcTo(x + w, y + h, x, y + h, r);
|
|
52
|
+
if (tail) { p.lineTo(tail[0] + 40, y + h); p.lineTo(tail[0], tail[1]); p.lineTo(tail[0] - 10, y + h); }
|
|
53
|
+
p.arcTo(x, y + h, x, y, r); p.arcTo(x, y, x + w, y, r); p.closePath(); return p;
|
|
54
|
+
};
|
|
55
|
+
const burst = (n, r0, r1) => { const p = new Path2D(); for (let i = 0; i < n * 2; i++) { const a = i / (n * 2) * Math.PI * 2, r = i % 2 ? r0 : r1; i ? p.lineTo(Math.cos(a) * r, Math.sin(a) * r) : p.moveTo(Math.cos(a) * r, Math.sin(a) * r); } p.closePath(); return p; };
|
|
56
|
+
const fatArrow = (c, x0, y0, x1, y1, p, col, w = 22) => { // outlined chunky arrow with hard shadow
|
|
57
|
+
if (p <= 0) return; const a = Math.atan2(y1 - y0, x1 - x0), l = Math.hypot(x1 - x0, y1 - y0) * p, hw = w * 1.3, hl = w * 1.6;
|
|
58
|
+
const path = new Path2D(); path.moveTo(0, -w / 2); path.lineTo(Math.max(0, l - hl), -w / 2); path.lineTo(Math.max(0, l - hl), -hw); path.lineTo(l, 0); path.lineTo(Math.max(0, l - hl), hw); path.lineTo(Math.max(0, l - hl), w / 2); path.lineTo(0, w / 2); path.closePath();
|
|
59
|
+
c.save(); c.translate(x0, y0); c.rotate(a); solid(c, path, col, 6, 4); c.restore();
|
|
60
|
+
};
|
|
61
|
+
const face = (c, kind, x, y, s) => { // chunky sticker card with a cat or dog face
|
|
62
|
+
const card = roundRect(x - s / 2, y - s / 2, s, s, s * 0.14); solid(c, card, PAL.cream, 6, 4);
|
|
63
|
+
c.lineWidth = 3.5; c.strokeStyle = K;
|
|
64
|
+
if (kind === 'cat') {
|
|
65
|
+
const p = new Path2D(); p.moveTo(x - s * 0.3, y + s * 0.02); p.lineTo(x - s * 0.28, y - s * 0.34); p.lineTo(x - s * 0.08, y - s * 0.18); p.lineTo(x + s * 0.08, y - s * 0.18); p.lineTo(x + s * 0.28, y - s * 0.34); p.lineTo(x + s * 0.3, y + s * 0.02); p.arc(x, y + s * 0.04, s * 0.3, 0, Math.PI); p.closePath();
|
|
66
|
+
c.fillStyle = PAL.yellow; c.fill(p); c.stroke(p);
|
|
67
|
+
c.fillStyle = K; c.beginPath(); c.arc(x - s * 0.1, y + s * 0.02, s * 0.04, 0, 7); c.arc(x + s * 0.1, y + s * 0.02, s * 0.04, 0, 7); c.fill();
|
|
68
|
+
c.fillStyle = PAL.magenta; c.beginPath(); c.moveTo(x - s * 0.05, y + s * 0.1); c.lineTo(x + s * 0.05, y + s * 0.1); c.lineTo(x, y + s * 0.16); c.closePath(); c.fill();
|
|
69
|
+
} else {
|
|
70
|
+
c.fillStyle = PAL.blue; for (const sd of [-1, 1]) { c.beginPath(); c.ellipse(x + sd * s * 0.28, y + s * 0.02, s * 0.1, s * 0.2, sd * -0.3, 0, 7); c.fill(); c.stroke(); }
|
|
71
|
+
c.fillStyle = PAL.pink; c.beginPath(); c.arc(x, y + s * 0.02, s * 0.25, 0, 7); c.fill(); c.stroke();
|
|
72
|
+
c.fillStyle = K; c.beginPath(); c.arc(x - s * 0.09, y - s * 0.02, s * 0.035, 0, 7); c.arc(x + s * 0.09, y - s * 0.02, s * 0.035, 0, 7); c.fill();
|
|
73
|
+
c.beginPath(); c.ellipse(x, y + s * 0.1, s * 0.06, s * 0.045, 0, 0, 7); c.fill();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const tag = (c, txt, x, y, col, size = 22, rot = 0, halo = 0) => { c.save(); c.translate(x, y); c.rotate(rot); c.font = `700 ${size}px "Fredoka"`; const w = c.measureText(txt).width + size; if (halo) { const hp = roundRect(-w / 2 - halo, -size * 0.75 - halo, w + halo * 2, size * 1.5 + halo * 2, size * 0.75 + halo); solid(c, hp, PAL.cream, 6, 3); } const p = roundRect(-w / 2, -size * 0.75, w, size * 1.5, size * 0.75); solid(c, p, col, 4, 3); c.fillStyle = K; c.textAlign = 'center'; c.textBaseline = 'middle'; c.fillText(txt, 0, 1); c.restore(); };
|
|
77
|
+
const chunky = (c, txt, x, y, size, col, ls = -4) => { c.save(); c.font = `400 ${size}px "Rubik Mono One"`; c.letterSpacing = ls + 'px'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.lineJoin = 'round'; c.fillStyle = K; c.fillText(txt, x + size * 0.07, y + size * 0.07); c.lineWidth = Math.max(4, size * 0.09); c.strokeStyle = K; c.strokeText(txt, x, y); c.fillStyle = col; c.fillText(txt, x, y); c.restore(); c.letterSpacing = '0px'; };
|
|
78
|
+
const machine = (c, x, y, knobs, look, mood) => { // the model: a Memphis totem-computer with a funnel and three knobs
|
|
79
|
+
// feet
|
|
80
|
+
for (const sd of [-1, 1]) { const f = new Path2D(); f.arc(x + sd * 60, y + 140, 34, Math.PI, 0); f.closePath(); solid(c, f, PAL.blue, 6, 4); }
|
|
81
|
+
const body = roundRect(x - 115, y - 120, 230, 260, 26); solid(c, body, PAL.lilac, 12, 5);
|
|
82
|
+
c.save(); c.clip(body); c.fillStyle = 'rgba(21,21,21,0.5)'; for (let i = -10; i < 10; i++) { c.beginPath(); c.arc(x - 100 + (i % 5) * 50, y + 80 + Math.floor(i / 5) * 18, 3, 0, 7); c.fill(); } c.restore();
|
|
83
|
+
const scr = roundRect(x - 85, y - 92, 170, 110, 18); solid(c, scr, PAL.cream, 0, 4);
|
|
84
|
+
const dx = (look - 0.5) * 16;
|
|
85
|
+
c.fillStyle = K; c.beginPath(); c.arc(x - 34 + dx, y - 42, 12, 0, 7); c.arc(x + 34 + dx, y - 42, 12, 0, 7); c.fill();
|
|
86
|
+
c.lineWidth = 5; c.strokeStyle = K; c.lineCap = 'round'; c.beginPath(); if (mood > 0) c.arc(x + dx * 0.5, y - 20, 22, 0.2, Math.PI - 0.2); else if (mood < 0) c.arc(x + dx * 0.5, y + 2, 20, Math.PI + 0.3, -0.3); else { c.moveTo(x - 18, y - 6); c.lineTo(x + 18, y - 6); } c.stroke();
|
|
87
|
+
// knobs on the belly
|
|
88
|
+
knobs.forEach((a, i) => { const kx = x - 62 + i * 62, ky = y + 50; const kp = new Path2D(); kp.arc(kx, ky, 20, 0, Math.PI * 2); solid(c, kp, [PAL.magenta, PAL.teal, PAL.yellow][i], 4, 4); c.save(); c.translate(kx, ky); c.rotate(a); c.fillStyle = K; c.fillRect(-3, -18, 6, 16); c.restore(); });
|
|
89
|
+
// funnel on top
|
|
90
|
+
const fn = new Path2D(); fn.moveTo(x - 80, y - 210); fn.lineTo(x + 80, y - 210); fn.lineTo(x + 26, y - 122); fn.lineTo(x - 26, y - 122); fn.closePath();
|
|
91
|
+
solid(c, fn, PAL.yellow, 8, 5); c.save(); c.clip(fn); c.fillStyle = K; for (let i = -6; i < 6; i++) c.fillRect(x + i * 24, y - 220, 9, 110); c.restore(); c.lineWidth = 5; c.strokeStyle = K; c.stroke(fn);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// static per-scene ground: pastel split, bacterio dashes at hero strength, and a decor layer (terrazzo quarter-disc,
|
|
95
|
+
// dot-grid card, confetti, a squiggle) kept to corners and edges that no story element uses. Built once per scene.
|
|
96
|
+
const GROUNDS = {};
|
|
97
|
+
const confetti = (c, r, spots) => spots.forEach(([x, y], i) => {
|
|
98
|
+
const s = r.range(10, 17), kind = i % 4, col = [PAL.magenta, PAL.blue, PAL.yellow, PAL.teal, PAL.red, K][(i * 5 + 1) % 6];
|
|
99
|
+
c.save(); c.translate(x, y); c.rotate(r.range(0, 7)); const p = new Path2D();
|
|
100
|
+
if (kind <= 1) { p.moveTo(0, -s); p.lineTo(s * 0.9, s * 0.6); p.lineTo(-s * 0.9, s * 0.6); p.closePath(); c.fillStyle = col; c.fill(p); c.lineWidth = 3; c.strokeStyle = K; c.stroke(p); }
|
|
101
|
+
else if (kind === 2) { c.fillStyle = col; c.fillRect(-s * 0.9, -3.5, s * 1.8, 7); }
|
|
102
|
+
else { c.beginPath(); c.arc(0, 0, s * 0.45, 0, 7); c.fillStyle = col; c.fill(); c.lineWidth = 3; c.strokeStyle = K; c.stroke(); }
|
|
103
|
+
c.restore();
|
|
104
|
+
});
|
|
105
|
+
const dotCard = (c, x, y, w, h, col, rot = 0) => { c.save(); c.translate(x + w / 2, y + h / 2); c.rotate(rot); const p = new Path2D(); p.rect(-w / 2, -h / 2, w, h); solid(c, p, col, 8, 4); c.save(); c.clip(p); c.fillStyle = K; for (let yy = -h / 2 + 14; yy < h / 2 - 6; yy += 16) for (let xx = -w / 2 + 14; xx < w / 2 - 6; xx += 16) { c.beginPath(); c.arc(xx, yy, 3, 0, 7); c.fill(); } c.restore(); c.restore(); };
|
|
106
|
+
const quarter = (c, cx, cy, R, a0, seed) => { const p = new Path2D(); p.moveTo(cx, cy); p.arc(cx, cy, R, a0, a0 + Math.PI / 2); p.closePath(); c.save(); c.translate(10, 10); c.fillStyle = K; c.fill(p); c.restore(); terrazzo(c, p, seed); c.lineWidth = 5; c.strokeStyle = K; c.stroke(p); };
|
|
107
|
+
const gridCard = (c, x, y, w, h, col, rot) => { c.save(); c.translate(x + w / 2, y + h / 2); c.rotate(rot); const p = new Path2D(); p.rect(-w / 2, -h / 2, w, h); solid(c, p, col, 8, 4); c.save(); c.clip(p); c.strokeStyle = 'rgba(21,21,21,0.5)'; c.lineWidth = 2; for (let xx = -w / 2; xx < w / 2; xx += 24) { c.beginPath(); c.moveTo(xx, -h / 2); c.lineTo(xx, h / 2); c.stroke(); } for (let yy = -h / 2; yy < h / 2; yy += 24) { c.beginPath(); c.moveTo(-w / 2, yy); c.lineTo(w / 2, yy); c.stroke(); } c.restore(); c.lineWidth = 4; c.strokeStyle = K; c.stroke(p); c.restore(); };
|
|
108
|
+
function sceneGround(scene, grounds, W, HH) {
|
|
109
|
+
const key = scene + ':' + W + 'x' + HH;
|
|
110
|
+
if (GROUNDS[key]) return GROUNDS[key];
|
|
111
|
+
const L = H.layer(W, HH), c = L.getContext('2d');
|
|
112
|
+
c.fillStyle = grounds[0]; c.fillRect(0, 0, W, HH);
|
|
113
|
+
c.fillStyle = grounds[1]; c.beginPath(); c.moveTo(860, 0); c.lineTo(W, 0); c.lineTo(W, HH); c.lineTo(600, HH); c.closePath(); c.fill();
|
|
114
|
+
const rb = H.rng(3 + scene); c.fillStyle = 'rgba(21,21,21,0.55)';
|
|
115
|
+
for (let i = 0; i < 260; i++) { const x = rb.range(0, W), y = rb.range(0, HH), a = rb.range(0, Math.PI); c.save(); c.translate(x, y); c.rotate(a); c.beginPath(); c.ellipse(0, 0, rb.range(3, 7), 1.6, 0, 0, 7); c.fill(); c.restore(); }
|
|
116
|
+
const r = H.rng(40 + scene);
|
|
117
|
+
c.lineCap = 'round'; c.lineJoin = 'round';
|
|
118
|
+
if (scene === 1) {
|
|
119
|
+
quarter(c, 0, HH, 165, -Math.PI / 2, 21);
|
|
120
|
+
dotCard(c, 1010, 638, 240, 62, PAL.butter, -0.02);
|
|
121
|
+
strokeOutlined(c, squigglePath(430, 688, 230, 9, 3.5, -0.03), PAL.blue, 8, 3, 5);
|
|
122
|
+
confetti(c, r, [[40, 170], [78, 300], [36, 430], [1240, 40], [1150, 70], [1250, 150], [470, 40], [600, 640], [1260, 400]]);
|
|
123
|
+
} else if (scene === 2) {
|
|
124
|
+
quarter(c, W, HH, 165, Math.PI, 22);
|
|
125
|
+
dotCard(c, 1132, 22, 128, 84, PAL.pink, 0.04);
|
|
126
|
+
strokeOutlined(c, squigglePath(620, 690, 240, 10, 3, -0.04), PAL.magenta, 8, 3, 5);
|
|
127
|
+
c.strokeStyle = K; c.lineWidth = 6; c.stroke(zigzagPath(48, 200, 300, 10, 9, Math.PI / 2));
|
|
128
|
+
confetti(c, r, [[40, 640], [110, 690], [380, 670], [900, 640], [880, 40], [1060, 40], [1250, 210], [1240, 300], [440, 40]]);
|
|
129
|
+
} else if (scene === 3) {
|
|
130
|
+
quarter(c, W, HH, 150, Math.PI, 23);
|
|
131
|
+
gridCard(c, 430, 30, 230, 96, PAL.butter, -0.03);
|
|
132
|
+
strokeOutlined(c, squigglePath(30, 680, 210, 9, 3, -0.03), PAL.teal, 8, 3, 5);
|
|
133
|
+
confetti(c, r, [[720, 50], [780, 120], [1250, 60], [1180, 40], [40, 200], [60, 330], [850, 690], [700, 690]]);
|
|
134
|
+
} else {
|
|
135
|
+
quarter(c, W, 0, 160, Math.PI / 2, 24);
|
|
136
|
+
dotCard(c, 560, 652, 240, 50, PAL.mint, 0.02);
|
|
137
|
+
c.strokeStyle = K; c.lineWidth = 6; c.stroke(squigglePath(52, 190, 300, 12, 3, Math.PI / 2));
|
|
138
|
+
confetti(c, r, [[860, 40], [960, 90], [1210, 260], [1240, 380], [1210, 520], [40, 60 + 560], [470, 690], [1240, 660], [760, 40]]);
|
|
139
|
+
}
|
|
140
|
+
GROUNDS[key] = L; return L;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const explainMemphis = (ctx, W, HH, t) => {
|
|
144
|
+
const c = ctx, E = H.ease, cl = H.clamp;
|
|
145
|
+
const tq = Math.floor(t * 12) / 12; // on twos
|
|
146
|
+
const S = (a, b) => cl((tq - a) / (b - a), 0, 1);
|
|
147
|
+
const pop = (d, x, y, dur = 0.25) => { const s = E.back(S(d, d + dur)); if (s <= 0) return false; c.save(); c.translate(x, y); c.scale(s, s); c.translate(-x, -y); return true; };
|
|
148
|
+
const bob = (ph, a = 5) => Math.sin(tq * 2.4 + ph) * a;
|
|
149
|
+
const BANDS = [5.4, 9.8, 12.35], swap = (b) => b + 0.42;
|
|
150
|
+
const scene = tq < swap(BANDS[0]) ? 1 : tq < swap(BANDS[1]) ? 2 : tq < swap(BANDS[2]) ? 3 : 4;
|
|
151
|
+
const grounds = { 1: [PAL.pink, PAL.mint], 2: [PAL.butter, PAL.lilac], 3: [PAL.mint, PAL.pink], 4: [PAL.lilac, PAL.butter] }[scene];
|
|
152
|
+
c.drawImage(sceneGround(scene, grounds, W, HH), 0, 0);
|
|
153
|
+
const MX = 300, MY = 430; // machine position in scenes 1-2
|
|
154
|
+
|
|
155
|
+
// ---------- hook: big bubble, then it shrinks into a corner sticker ----------
|
|
156
|
+
const hp = E.inOut(S(1.7, 2.3));
|
|
157
|
+
{
|
|
158
|
+
const bp = S(0.1, 0.45); const x = H.lerp(640, 200, hp), y = H.lerp(340, 80, hp), sc = H.lerp(1, 0.42, hp), rot = H.lerp(-0.05, -0.06, hp);
|
|
159
|
+
if (bp > 0 && hp < 1) {
|
|
160
|
+
c.save(); c.translate(x, y); c.rotate(rot); c.scale(sc * E.back(bp), sc * E.back(bp));
|
|
161
|
+
solid(c, bubble(820, 300, hp < 0.5 ? [-200, 230] : null), PAL.yellow, 16, 6);
|
|
162
|
+
if (tq >= 0.45) chunky(c, 'HOW DOES', 0, -50, 84, PAL.magenta);
|
|
163
|
+
if (tq >= 0.6) chunky(c, 'AI LEARN?', 0, 60, 84, PAL.blue);
|
|
164
|
+
c.restore();
|
|
165
|
+
}
|
|
166
|
+
if (hp < 1) { strokeOutlined(c, squigglePath(80, 620, 260, 16, 3, -0.1, tq * 3, S(0.3, 0.9)), PAL.teal, 12, 4, 6); if (pop(0.5, 1120, 150)) { c.translate(1120, 150); c.rotate(tq); solid(c, burst(12, 50, 80), PAL.magenta, 8, 4); c.restore(); } }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------- scene 1: data dropped into the funnel ----------
|
|
170
|
+
if (scene === 1 && tq > 2.0) {
|
|
171
|
+
if (pop(2.0, MX, MY, 0.3)) { machine(c, MX, MY + bob(0, 3), [0.4, -0.8, 1.2], 0.5 + 0.5 * Math.sin(tq * 3), 1); c.restore(); }
|
|
172
|
+
if (tq > 2.3) tag(c, 'MODEL', MX, MY + 200, PAL.butter, 28, -0.05);
|
|
173
|
+
// examples hop in from the right along arcs into the funnel
|
|
174
|
+
for (let i = 0; i < 12; i++) {
|
|
175
|
+
const u = (tq - 2.5 - i * 0.28) / 1.1; if (u < 0 || u > 1) continue;
|
|
176
|
+
const kind = H.rng(i * 9 + 4)() < 0.5 ? 'cat' : 'dog', sx = 1180 - (i % 3) * 120, sy = 560 - (i % 2) * 90;
|
|
177
|
+
const x = H.lerp(sx, MX, u), y = H.lerp(sy, MY - 230, u) - Math.sin(u * Math.PI) * 260, s = 92 * (1 - E.in(cl((u - 0.75) / 0.25, 0, 1)));
|
|
178
|
+
if (s < 8) continue;
|
|
179
|
+
c.save(); c.translate(x, y); c.rotate((1 - u) * (i % 2 ? 0.4 : -0.4)); face(c, kind, 0, 0, s); if (s > 60) tag(c, kind, 0, s * 0.58, kind === 'cat' ? PAL.yellow : PAL.mint, 28); c.restore();
|
|
180
|
+
}
|
|
181
|
+
fatArrow(c, 1000, 250, 520, 200, E.out(S(2.7, 3.1)), PAL.magenta, 20);
|
|
182
|
+
if (tq > 3.0) tag(c, 'TRAINING DATA', 800, 160, PAL.cream, 28, -0.08, 9);
|
|
183
|
+
// counter sticker pops every time it ticks
|
|
184
|
+
const cp = S(3.0, 5.2), n = cp < 1 ? Math.pow(cp, 3) * 1e6 : 1e6, beat = 1 + 0.06 * (Math.floor(tq * 12) % 2);
|
|
185
|
+
if (pop(2.9, 960, 520, 0.3)) {
|
|
186
|
+
c.translate(960, 520); c.rotate(0.05); c.scale(beat, beat);
|
|
187
|
+
const st = roundRect(-250, -70, 500, 140, 30); solid(c, st, PAL.cream, 10, 5);
|
|
188
|
+
chunky(c, Math.round(n).toLocaleString('en-US'), 0, -12, 64, PAL.red, -6);
|
|
189
|
+
c.font = '700 30px "Fredoka"'; c.fillStyle = K; c.textAlign = 'center'; c.fillText('EXAMPLES', 0, 50);
|
|
190
|
+
c.restore();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---------- scene 2: guess -> check -> adjust ----------
|
|
195
|
+
if (scene === 2) {
|
|
196
|
+
const t0 = swap(BANDS[0]), adj = E.inOut(S(8.3, 9.0)), right = tq >= 9.3;
|
|
197
|
+
const knobs = [H.lerp(0.4, -1.2, adj), H.lerp(-0.8, 0.9, adj), H.lerp(1.2, 0.1, adj)];
|
|
198
|
+
machine(c, MX, MY + bob(0, 3), knobs, right ? 0.9 : 0.8, tq < 7.4 ? 0 : right ? 1 : -1);
|
|
199
|
+
// new picture
|
|
200
|
+
if (pop(t0 + 0.1, 560, 470, 0.3)) { face(c, 'cat', 560, 470, 130); c.restore(); }
|
|
201
|
+
if (tq > t0 + 0.35) tag(c, 'NEW PICTURE', 560, 570, PAL.cream, 24, 0.04);
|
|
202
|
+
// speech bubble with the guess
|
|
203
|
+
const bx = 600, by = 200;
|
|
204
|
+
const say = (txt, col, d) => { const s = E.back(S(d, d + 0.25)); if (s <= 0) return false; c.save(); c.translate(bx, by); c.scale(s, s); solid(c, bubble(300, 130, [-110, 110]), PAL.cream, 8, 5); chunky(c, txt, 0, 4, 60, col); c.restore(); return true; };
|
|
205
|
+
if (!right && say('DOG?', PAL.blue, 6.7)) {
|
|
206
|
+
if (tq >= 7.4) { // CHECK: starburst + squiggly cross
|
|
207
|
+
c.save(); c.translate(bx + 200, by - 40); c.rotate(tq * 0.8); solid(c, burst(10, 44, 70), PAL.red, 6, 4); c.restore();
|
|
208
|
+
c.save(); c.translate(bx + 200, by - 40); c.lineCap = 'round'; c.strokeStyle = PAL.cream; c.lineWidth = 12; c.beginPath(); c.moveTo(-22, -22); c.lineTo(22, 22); c.moveTo(22, -22); c.lineTo(-22, 22); c.stroke(); c.restore();
|
|
209
|
+
tag(c, 'NOPE, IT’S A CAT!', bx + 20, by - 102, PAL.red, 26, -0.04);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (right && say('CAT!', PAL.magenta, 9.3)) {
|
|
213
|
+
if (tq >= 9.6) { c.save(); c.translate(bx + 190, by - 40); solid(c, burst(10, 44, 66), PAL.teal, 6, 4); c.lineCap = 'round'; c.lineJoin = 'round'; c.strokeStyle = K; c.lineWidth = 11; c.beginPath(); c.moveTo(-22, 2); c.lineTo(-6, 18); c.lineTo(24, -18); c.stroke(); c.restore(); }
|
|
214
|
+
}
|
|
215
|
+
// ADJUST: a zigzag bolt runs back from the check to the knobs
|
|
216
|
+
if (tq >= 7.8 && tq < 9.3) { strokeOutlined(c, zigzagPath(bx + 190, by + 10, 480, 18, 10, 2.45, S(7.8, 8.2)), PAL.yellow, 11, 4, 6); if (tq > 8.2) tag(c, 'ERROR!', 520, 380, PAL.yellow, 26, 0.1); }
|
|
217
|
+
// the loop on the right: three patterned shapes chased by chunky arrows
|
|
218
|
+
const L = [['GUESS', 1000, 170, 'circle', PAL.magenta], ['CHECK', 1160, 400, 'tri', PAL.teal], ['ADJUST', 900, 470, 'square', PAL.blue]], act = tq < 7.4 ? 0 : tq < 8.2 ? 1 : tq < 9.3 ? 2 : Math.floor((tq - 9.3) * 3) % 3;
|
|
219
|
+
L.forEach(([s, x, y, sh, col], i) => {
|
|
220
|
+
if (!pop(t0 + 0.2 + i * 0.12, x, y, 0.25)) return;
|
|
221
|
+
const k = i === act && tq > 6.7 ? 1.12 + 0.04 * Math.sin(tq * 12) : 1; c.translate(x, y); c.scale(k, k); c.translate(-x, -y);
|
|
222
|
+
const p = new Path2D(); if (sh === 'circle') p.arc(x, y, 62, 0, 7); else if (sh === 'tri') { p.moveTo(x, y - 70); p.lineTo(x + 70, y + 52); p.lineTo(x - 70, y + 52); p.closePath(); } else p.rect(x - 60, y - 55, 120, 110);
|
|
223
|
+
solid(c, p, col, 8, 5);
|
|
224
|
+
c.save(); c.clip(p); c.fillStyle = 'rgba(21,21,21,0.35)'; for (let a = -4; a < 5; a++) for (let b = -4; b < 5; b++) { c.beginPath(); c.arc(x + a * 18, y + b * 18, 2.6, 0, 7); c.fill(); } c.restore();
|
|
225
|
+
tag(c, s, x, y + (sh === 'tri' ? 24 : 4), PAL.cream, 24); c.restore();
|
|
226
|
+
});
|
|
227
|
+
fatArrow(c, 1050, 225, 1120, 320, E.out(S(7.1, 7.35)), PAL.yellow, 18);
|
|
228
|
+
fatArrow(c, 1090, 450, 975, 470, E.out(S(7.9, 8.15)), PAL.yellow, 18);
|
|
229
|
+
fatArrow(c, 880, 400, 945, 240, E.out(S(9.0, 9.25)), PAL.yellow, 18);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------- scene 3: progress ----------
|
|
233
|
+
if (scene === 3) {
|
|
234
|
+
const t0 = swap(BANDS[1]), prog = E.inOut(S(t0 + 0.2, 11.9)), base = 600, x0 = 110;
|
|
235
|
+
const ax = new Path2D(); ax.rect(x0 - 10, base, 700, 12); solid(c, ax, K, 0, 0);
|
|
236
|
+
for (let i = 0; i < 8; i++) {
|
|
237
|
+
const acc = 61 + 36 * (1 - Math.pow(1 - i / 7, 2)), bp = E.back(cl(prog * 8 - i, 0, 1)), h = (acc - 40) * 7 * bp; if (h <= 1) continue;
|
|
238
|
+
const p = new Path2D(); p.rect(x0 + 10 + i * 84, base - h, 62, h); solid(c, p, [PAL.magenta, PAL.blue, PAL.yellow, PAL.teal][i % 4], 7, 4);
|
|
239
|
+
c.save(); c.clip(p); if (i % 2) { c.fillStyle = 'rgba(21,21,21,0.5)'; for (let y = base - h; y < base; y += 16) c.fillRect(x0 + 10 + i * 84, y, 62, 6); } else { c.fillStyle = PAL.cream; for (let y = base - h + 8; y < base; y += 18) for (let x = 0; x < 3; x++) { c.beginPath(); c.arc(x0 + 22 + i * 84 + x * 19, y, 3.5, 0, 7); c.fill(); } } c.restore();
|
|
240
|
+
}
|
|
241
|
+
if (tq > t0 + 0.2) tag(c, 'TRAINING ROUNDS →', 440, 655, PAL.cream, 26);
|
|
242
|
+
// emphasis burst + confetti behind the number
|
|
243
|
+
const em = S(11.9, 12.1);
|
|
244
|
+
if (em > 0) { c.save(); c.translate(1010, 330); c.rotate(tq * 0.6); c.scale(E.back(em), E.back(em)); solid(c, burst(16, 150, 215), PAL.yellow, 12, 5); c.restore();
|
|
245
|
+
const rc = H.rng(5); for (let i = 0; i < 18; i++) { const a = rc.range(0, 7), d = 200 + rc.range(0, 120) * em; c.save(); c.translate(1010 + Math.cos(a) * d, 330 + Math.sin(a) * d); c.rotate(a + tq * 3); c.fillStyle = rc.pick([PAL.magenta, PAL.blue, PAL.teal, K]); c.beginPath(); c.moveTo(0, -12); c.lineTo(11, 8); c.lineTo(-11, 8); c.closePath(); c.fill(); c.restore(); } }
|
|
246
|
+
if (pop(t0 + 0.1, 1010, 330, 0.3)) { chunky(c, Math.round(H.lerp(61, 97, prog)) + '%', 1010, 330, 120, PAL.magenta, -10); c.restore(); }
|
|
247
|
+
if (tq > t0 + 0.3) tag(c, 'ACCURACY', 1010, 200, PAL.cream, 26, -0.05);
|
|
248
|
+
if (tq > t0 + 0.5) tag(c, '61% → 97%', 1010, 450, PAL.mint, 24, 0.04);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ---------- scene 4: takeaway ----------
|
|
252
|
+
if (scene === 4) {
|
|
253
|
+
const t0 = swap(BANDS[2]);
|
|
254
|
+
if (pop(t0 + 0.05, 640, 360, 0.3)) {
|
|
255
|
+
c.translate(640, 360); c.rotate(-0.04 + Math.sin(tq * 2.4) * 0.01);
|
|
256
|
+
solid(c, bubble(1000, 360, [-300, 250]), PAL.yellow, 16, 6);
|
|
257
|
+
chunky(c, 'MORE EXAMPLES', 0, -70, 68, PAL.blue, -6);
|
|
258
|
+
if (tq >= t0 + 0.35) chunky(c, 'BETTER GUESSES!', 0, 80, 68, PAL.magenta, -6);
|
|
259
|
+
c.restore();
|
|
260
|
+
}
|
|
261
|
+
fatArrow(c, 640, 330, 640, 380, E.out(S(t0 + 0.2, t0 + 0.4)), PAL.teal, 16);
|
|
262
|
+
strokeOutlined(c, squigglePath(120, 640, 280, 16, 3, -0.08, tq * 3, S(t0 + 0.3, t0 + 0.8)), PAL.magenta, 12, 4, 6);
|
|
263
|
+
strokeOutlined(c, zigzagPath(900, 640, 260, 16, 9, -0.05, S(t0 + 0.4, t0 + 0.9)), PAL.teal, 11, 4, 6);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ---------- corner sticker header (after the hook) ----------
|
|
267
|
+
|
|
268
|
+
// ---------- striped band swipe between scenes ----------
|
|
269
|
+
for (const b of BANDS) {
|
|
270
|
+
const u = S(b, b + 0.85); if (u <= 0 || u >= 1) continue;
|
|
271
|
+
const x = H.lerp(-2300, W + 900, u);
|
|
272
|
+
c.save(); c.translate(x, HH / 2); c.rotate(-0.35);
|
|
273
|
+
const band = new Path2D(); band.rect(0, -1400, 1700, 2800);
|
|
274
|
+
c.save(); c.translate(14, 14); c.fillStyle = K; c.fill(band); c.restore();
|
|
275
|
+
c.save(); c.clip(band); const cols = [PAL.magenta, PAL.yellow, PAL.blue, PAL.mint, PAL.red];
|
|
276
|
+
for (let i = 0; i < 20; i++) { c.fillStyle = cols[i % cols.length]; c.fillRect(i * 85, -1400, 86, 2800); c.fillStyle = K; for (let y = -1400; y < 1400; y += 40) { c.beginPath(); c.arc(i * 85 + 42, y + (i % 2) * 20, 5, 0, 7); c.fill(); } }
|
|
277
|
+
c.restore(); c.lineWidth = 6; c.strokeStyle = K; c.stroke(band); c.restore();
|
|
278
|
+
}
|
|
279
|
+
// header sticker stays on top
|
|
280
|
+
if (hp >= 1) { c.save(); c.translate(200, 80); c.rotate(-0.06); c.scale(0.42, 0.42); solid(c, bubble(820, 300, null), PAL.yellow, 16, 6); chunky(c, 'HOW DOES', 0, -50, 84, PAL.magenta); chunky(c, 'AI LEARN?', 0, 60, 84, PAL.blue); c.restore(); }
|
|
281
|
+
H.grain(ctx, W, HH, 0.05, 6 + Math.floor(t * 12), true);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
registerStyle({
|
|
285
|
+
id: 'memphis',
|
|
286
|
+
name: 'Memphis Group',
|
|
287
|
+
category: 'Art & Design Movements',
|
|
288
|
+
description: 'Early-80s Memphis Milano maximalism: pastel grounds clashing with bold primaries, thick black outlines and hard offset shadows, squiggles, zigzags, dot grids, terrazzo chips and confetti triangles, all orbiting a chunky tilted chat-bubble headline.',
|
|
289
|
+
motion: 'Cut-out cel timing on twos: the mint wedge slides in, shapes pop on one by one with springy overshoot, squiggles and zigzags draw themselves, the chat bubble squash-and-stretches in, the words pop on the beat, confetti bursts out and the sticker slaps on; then everything bobs off-beat and the typing dots bounce.',
|
|
290
|
+
fonts: ['400 92px "Rubik Mono One"', '700 26px "Fredoka"'],
|
|
291
|
+
frameT: 4.1,
|
|
292
|
+
duration: 4.5,
|
|
293
|
+
readsPixels: true,
|
|
294
|
+
explainerDuration: 14.5,
|
|
295
|
+
explainerNote: 'Memphis cut-out animation on twos: examples hop into a funnel-topped totem computer, scenes change with a striped polka-dot band that swipes across, arrows are fat outlined shapes with hard shadows, counters are stickers that pop on each tick, and the key moments get a spinning starburst.',
|
|
296
|
+
explainer(ctx, W, HH, t) { explainMemphis(ctx, W, HH, t); },
|
|
297
|
+
// Sound: a playful 80s cut-out cartoon. Stickers and shapes land with rubbery pops, things hop on springy boings,
|
|
298
|
+
// the striped band swipes across as a fluttering zip, labels get bright synth stabs, the wrong guess is a sad
|
|
299
|
+
// "wah-wah" and the zigzag a buzzy zap, under a light synth-pop pulse. Times copied from explainMemphis(); it
|
|
300
|
+
// animates on twos at 12 fps, so q(x) is the first frame at which its quantised clock reaches x.
|
|
301
|
+
explainerSound(k) {
|
|
302
|
+
const D = 14.5, E = H.ease, cl = H.clamp;
|
|
303
|
+
const q = (x) => Math.ceil(x * 12 - 1e-6) / 12;
|
|
304
|
+
const land = (d, dur) => q(d + dur * 0.37); // back-eased pop(d, dur) first reaches full size at 37%
|
|
305
|
+
const pan = (px) => cl((px / 1280) * 2 - 1, -1, 1) * 0.7;
|
|
306
|
+
const when = (a, b, ease, u) => { for (let i = 0; i <= 400; i++) if (ease(i / 400) >= u - 1e-6) return q(a + ((b - a) * i) / 400); return q(b); };
|
|
307
|
+
const BANDS = [5.4, 9.8, 12.35], swap = (b) => b + 0.42, MX_ = 300; // MX_: the machine's x in scenes 1-2
|
|
308
|
+
// rubbery sticker pop (kept low)
|
|
309
|
+
const pop = (t, f, g, p, minor = true) => k.play('pop', t, { f, gain: g, pan: p, minor });
|
|
310
|
+
// springy boing: quick upward bend that sags back
|
|
311
|
+
const boing = (t, f, g, p, minor = true) => { k.tone(t, { f: [[0, f], [0.04, f * 2.4], [0.3, f * 1.3]], type: 'triangle', dur: 0.32, gain: 0.12 * g, a: 0.003, pan: p, minor }); k.tone(t, { f: [[0, f * 2], [0.05, f * 3.5], [0.2, f * 2.2]], dur: 0.2, gain: 0.03 * g, pan: p, minor }); };
|
|
312
|
+
// bright 80s synth stab (detuned saws through a closing filter)
|
|
313
|
+
const stab = (t, notes, g, p, minor) => notes.forEach((n, i) => { for (const det of [-10, 10])
|
|
314
|
+
k.tone(t, { f: n, type: 'sawtooth', detune: det, dur: 0.28, gain: 0.03 * g, a: 0.004, hold: 0.05, filter: 'lowpass', f2: [[0, 2600], [0.25, 700]], q: 2, pan: p + (i - 1) * 0.15, verb: 0.2, minor }); });
|
|
315
|
+
// the striped band: a fluttering zip as ~20 stripes rush past, left to right
|
|
316
|
+
const zip = (b) => {
|
|
317
|
+
const d = 0.85;
|
|
318
|
+
k.tone(b + 0.1, { f: [[0, 160], [d - 0.15, 520]], type: 'sawtooth', dur: d - 0.1, gain: 0.04, a: 0.25, filter: 'lowpass', f2: 1200, q: 3 });
|
|
319
|
+
for (let x = 0.08; x < d - 0.05; x += 1 / 28) { const e = Math.sin((x / d) * Math.PI);
|
|
320
|
+
k.noise(b + x, { dur: 0.025, filter: 'bandpass', f: 800 + e * 700, q: 1.5, gain: 0.16 * e, a: 0.002, pan: (x / d - 0.5) * 1.4 }); }
|
|
321
|
+
};
|
|
322
|
+
// a cartoon hand clap
|
|
323
|
+
const clap = (t, g, p) => [0, 0.012, 0.022].forEach((o) => k.noise(t + o, { dur: 0.05, filter: 'bandpass', f: 1300, q: 1.1, gain: 0.22 * g, a: 0.001, pan: p }));
|
|
324
|
+
|
|
325
|
+
k.music('pulse', 0, D, { bpm: 112, gain: 0.3, chords: [['C4', 'E4', 'G4', 'B4'], ['A3', 'C4', 'E4', 'G4'], ['F3', 'A3', 'C4', 'E4'], ['G3', 'B3', 'D4', 'F4']] });
|
|
326
|
+
|
|
327
|
+
// ---- hook: bubble pops up (back 0.1-0.45), HOW DOES (0.5), AI LEARN? + starburst (0.667), squiggle draws (0.3-0.9)
|
|
328
|
+
pop(land(0.1, 0.35), 360, 1.1, 0, false); boing(land(0.1, 0.35), 120, 0.8, 0, false);
|
|
329
|
+
stab(q(0.45), ['E4', 'G4', 'C5'], 0.9, -0.1, true);
|
|
330
|
+
stab(q(0.6), ['G4', 'C5', 'E5'], 1.1, 0.1, false); pop(q(0.6), 480, 0.8, pan(1120));
|
|
331
|
+
k.tone(0.32, { f: [[0, 140], [0.3, 220], [0.6, 170]], type: 'triangle', dur: 0.6, gain: 0.05, a: 0.1, filter: 'lowpass', f2: 900, pan: pan(200), minor: true }); // squiggle wiggles on
|
|
332
|
+
// bubble shrinks into the corner sticker (1.7-2.3): soft slide
|
|
333
|
+
k.noise(q(1.7), { dur: 0.6, color: 'pink', filter: 'bandpass', f: [[0, 700], [0.6, 350]], q: 0.8, gain: 0.07, a: 0.2, pan: -0.4, minor: true });
|
|
334
|
+
|
|
335
|
+
// ---- scene 1: DATA hops into the funnel
|
|
336
|
+
boing(land(2.0, 0.3), 90, 0.7, pan(MX_), false); k.play('thud', land(2.0, 0.3), { f: 120, gain: 0.2, pan: pan(MX_) });
|
|
337
|
+
pop(q(2.3 + 1e-3), 420, 0.6, pan(MX_)); // MODEL tag
|
|
338
|
+
const hops = []; for (let i = 0; 2.5 + i * 0.28 < 5.3; i++) hops.push(q(2.5 + i * 0.28));
|
|
339
|
+
hops.forEach((t, i) => boing(t, 150 + (i % 3) * 25, 0.55, pan(1180 - (i % 3) * 120)));
|
|
340
|
+
k.play('slap', when(2.7, 3.1, E.out, 0.95), { gain: 0.4, pan: pan(600), minor: true }); // fat arrow lands
|
|
341
|
+
pop(q(3.0 + 1e-3), 520, 0.8, pan(880)); // TRAINING DATA tag + counter sticker
|
|
342
|
+
// counter sticker swells on every other frame (6/s) while n = S(3.0,5.2)^3 * 1e6 ticks over
|
|
343
|
+
for (let f = Math.ceil(3.1 * 12); f / 12 < 5.2; f++) { const t = f / 12; if (f % 2 !== 1 || hops.some((h) => Math.abs(t - h) < 0.04)) continue;
|
|
344
|
+
pop(t, 640, 0.42, pan(960)); k.play('tick', t, { f: 2200, gain: 0.5, pan: pan(960), minor: true }); }
|
|
345
|
+
stab(q(5.2), ['C4', 'E4', 'G4', 'C5'], 1.2, pan(960)); pop(q(5.2), 300, 1, pan(960), false); // 1,000,000
|
|
346
|
+
|
|
347
|
+
// ---- the striped band swipes: 5.4, 9.8, 12.35
|
|
348
|
+
BANDS.forEach(zip);
|
|
349
|
+
|
|
350
|
+
// ---- scene 2: GUESS / CHECK / ADJUST (t0 = 5.82)
|
|
351
|
+
const t0 = swap(BANDS[0]);
|
|
352
|
+
pop(land(t0 + 0.1, 0.3), 380, 0.9, pan(560)); // new picture
|
|
353
|
+
[0, 1, 2].forEach((i) => pop(land(t0 + 0.2 + i * 0.12, 0.25), [440, 520, 600][i], 0.6, pan([1000, 1160, 900][i]))); // loop shapes
|
|
354
|
+
// the guess: DOG? bubble (back 6.7-6.95) with a questioning two-note blip
|
|
355
|
+
pop(land(6.7, 0.25), 340, 0.9, pan(600), false);
|
|
356
|
+
k.tone(land(6.7, 0.25) + 0.05, { f: 'D4', type: 'square', dur: 0.1, gain: 0.05, a: 0.003, hold: 0.06, filter: 'lowpass', f2: 1400, pan: pan(600) });
|
|
357
|
+
k.tone(land(6.7, 0.25) + 0.16, { f: 'A4', type: 'square', dur: 0.14, gain: 0.05, a: 0.003, hold: 0.08, filter: 'lowpass', f2: 1400, pan: pan(600) });
|
|
358
|
+
k.play('slap', when(7.1, 7.35, E.out, 0.95), { gain: 0.3, pan: pan(1090), minor: true });
|
|
359
|
+
// CHECK (7.417): starburst + cross + NOPE — a sad wah-wah
|
|
360
|
+
const ck = q(7.4);
|
|
361
|
+
pop(ck, 260, 1.1, pan(800), false); k.play('thud', ck, { f: 110, gain: 0.4, pan: pan(800) });
|
|
362
|
+
[['G3', 'F#3'], ['F#3', 'E3']].forEach(([a, b], i) => k.tone(ck + 0.05 + i * 0.24, { f: [[0, a], [0.2, b]], type: 'sawtooth', dur: i ? 0.45 : 0.22, gain: 0.12, a: 0.02, hold: 0.12, filter: 'lowpass', f2: [[0, 500], [0.1, 1300], [0.22, 500]], q: 3, pan: pan(800) }));
|
|
363
|
+
// ADJUST: the zigzag bolt zaps back to the knobs (7.8-8.2), ERROR! tag (8.25)
|
|
364
|
+
for (let x = 0; x < 0.4; x += 0.04) k.tone(q(7.8) + x, { f: 70 + (x * 37 % 1) * 60, type: 'sawtooth', dur: 0.035, gain: 0.07, a: 0.002, filter: 'bandpass', f2: 1500, q: 1.5, pan: pan(700 - x * 600) });
|
|
365
|
+
pop(q(8.2 + 1e-3), 420, 0.7, pan(520));
|
|
366
|
+
// knobs twist (inOut 8.3-9.0): ratchet clicks at least 90 ms apart
|
|
367
|
+
for (let i = 1, lt = 0; i <= 8; i++) { const t = when(8.3, 9.0, E.inOut, i / 8); if (t - lt < 0.09) continue; lt = t; k.play('click', t, { f: 1100, gain: 0.6, pan: pan(MX_), minor: true }); k.play('tock', t + 0.004, { f: 500 + i * 20, gain: 0.25, pan: pan(MX_), minor: true }); }
|
|
368
|
+
k.play('slap', when(9.0, 9.25, E.out, 0.95), { gain: 0.3, pan: pan(910), minor: true });
|
|
369
|
+
// the correction: CAT! bubble (9.417), teal starburst + tick (9.667) — a bouncy major arpeggio
|
|
370
|
+
pop(land(9.3, 0.25), 420, 0.9, pan(600));
|
|
371
|
+
['C4', 'E4', 'G4', 'C5'].forEach((n, i) => k.tone(q(9.6) + i * 0.06, { f: n, type: 'triangle', dur: 0.35, gain: 0.13, a: 0.003, pan: pan(790), verb: 0.2 }));
|
|
372
|
+
pop(q(9.6), 500, 0.9, pan(790), false);
|
|
373
|
+
|
|
374
|
+
// ---- scene 3: PROGRESS (t0 = 10.22): number pops (10.5), bars spring up one by one as prog = inOut(S(10.42,11.9)) passes i/8
|
|
375
|
+
const t3 = swap(BANDS[1]);
|
|
376
|
+
pop(land(t3 + 0.1, 0.3), 330, 0.9, pan(1010), false);
|
|
377
|
+
const bars = []; for (let i = 0; i < 8; i++) { const t = when(t3 + 0.2, 11.9, E.inOut, i / 8 + 0.02); if (bars.length && t - bars[bars.length - 1] < 0.09) continue; bars.push(t); boing(t, 110 + i * 14, 0.6, pan(150 + i * 84)); }
|
|
378
|
+
for (let f = Math.ceil(10.5 * 12), last = 61; f / 12 < 11.9; f++) { const t = f / 12, v = Math.round(H.lerp(61, 97, E.inOut(cl((t - t3 - 0.2) / (11.9 - t3 - 0.2), 0, 1))));
|
|
379
|
+
if (v !== last && !bars.some((b) => Math.abs(t - b) < 0.04)) { pop(t, 680, 0.38, pan(1010)); k.play('tick', t, { f: 2200, gain: 0.5, pan: pan(1010), minor: true }); last = v; } }
|
|
380
|
+
// 97%: yellow starburst springs out with confetti (12.0) — the biggest hit
|
|
381
|
+
const big = land(11.9, 0.2);
|
|
382
|
+
k.play('impact', big, { gain: 1.7, pan: pan(1010) }); k.play('thud', big, { f: 70, gain: 0.6 });
|
|
383
|
+
stab(big, ['C4', 'E4', 'G4', 'C5'], 1.6, pan(1010));
|
|
384
|
+
clap(big, 1, pan(1010)); boing(big, 80, 1.2, pan(1010), false);
|
|
385
|
+
for (let i = 0; i < 6; i++) pop(big + 0.3 + i * 0.08, 280 + (i * 97) % 300, 0.6, (i % 2 ? 0.4 : -0.4) + pan(1010) * 0.5); // confetti flies out
|
|
386
|
+
|
|
387
|
+
// ---- scene 4: TAKEAWAY (t0 = 12.77): bubble pops (13.0), arrow, BETTER GUESSES! (13.167), squiggle + zigzag draw on
|
|
388
|
+
const t4 = swap(BANDS[2]);
|
|
389
|
+
pop(land(t4 + 0.05, 0.3), 340, 1.0, 0, false); boing(land(t4 + 0.05, 0.3), 110, 0.7, 0);
|
|
390
|
+
k.tone(q(t4 + 0.3), { f: [[0, 150], [0.5, 230]], type: 'triangle', dur: 0.5, gain: 0.04, a: 0.1, filter: 'lowpass', f2: 900, pan: -0.5, minor: true });
|
|
391
|
+
stab(q(t4 + 0.35), ['F4', 'A4', 'C5'], 0.8, 0.1, true);
|
|
392
|
+
['C4', 'G4', 'C5', 'E5'].forEach((n, i) => k.tone(q(t4 + 0.35) + 0.25 + i * 0.07, { f: n, type: 'triangle', dur: 0.8, gain: 0.12 - i * 0.015, a: 0.003, pan: (i - 1.5) * 0.25, verb: 0.3 })); // resolves
|
|
393
|
+
clap(q(t4 + 0.35) + 0.25, 0.6, 0);
|
|
394
|
+
},
|
|
395
|
+
draw(ctx, W, HH, t) {
|
|
396
|
+
const c = ctx;
|
|
397
|
+
// cel-style timing: all motion is held for two frames (12 fps steps) like 80s cut-out animation
|
|
398
|
+
const tq = Math.floor(t * 12) / 12;
|
|
399
|
+
const bob = (ph, a = 6) => Math.sin(tq * 2.1 + ph) * a;
|
|
400
|
+
const P = (d, dur = 0.5) => H.clamp((tq - d) / dur, 0, 1);
|
|
401
|
+
const pop = (d, x, y, dur = 0.25) => { const s = H.ease.back(P(d, dur)); if (s <= 0) return false; c.save(); c.translate(x, y); c.scale(s, s); c.translate(-x, -y); return true; };
|
|
402
|
+
|
|
403
|
+
// ---------- ground: pastel split (mint wedge slides in) ----------
|
|
404
|
+
c.fillStyle = PAL.pink; c.fillRect(0, 0, W, HH);
|
|
405
|
+
{ const off = (1 - H.ease.out(P(0, 0.33))) * 760;
|
|
406
|
+
c.fillStyle = PAL.mint; c.beginPath(); c.moveTo(820 + off, 0); c.lineTo(W + off, 0); c.lineTo(W + off, HH); c.lineTo(560 + off, HH); c.closePath(); c.fill(); }
|
|
407
|
+
// fine black dash texture on the pink (classic Memphis "bacterio"-lite), sprinkled on
|
|
408
|
+
const rb = H.rng(3), nb = Math.floor(260 * P(0.15, 0.4));
|
|
409
|
+
c.fillStyle = 'rgba(21,21,21,0.55)';
|
|
410
|
+
for (let i = 0; i < 260; i++) {
|
|
411
|
+
const x = rb.range(0, 760), y = rb.range(0, HH); const a = rb.range(0, Math.PI), l = rb.range(3, 7);
|
|
412
|
+
if (i >= nb) continue;
|
|
413
|
+
c.save(); c.translate(x, y); c.rotate(a); c.beginPath(); c.ellipse(0, 0, l, 1.6, 0, 0, 7); c.fill(); c.restore();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ---------- background shapes, popping in one by one ----------
|
|
417
|
+
// terrazzo semicircle dropping from the top-left
|
|
418
|
+
{ const dy = -(1 - H.ease.back(P(0.25, 0.3))) * 230;
|
|
419
|
+
const semi = new Path2D(); semi.arc(210, -10 + bob(0.3, 4) + dy, 190, 0, Math.PI); semi.closePath();
|
|
420
|
+
c.save(); c.translate(10, 10); c.fillStyle = K; c.fill(semi); c.restore();
|
|
421
|
+
terrazzo(c, semi, 11); c.lineWidth = 5; c.strokeStyle = K; c.stroke(semi); }
|
|
422
|
+
|
|
423
|
+
// dot grid panel (top right)
|
|
424
|
+
if (pop(0.42, 1065, 139)) {
|
|
425
|
+
c.translate(0, bob(1.2, 3));
|
|
426
|
+
const dg = new Path2D(); dg.rect(930, 54, 270, 170);
|
|
427
|
+
solid(c, dg, PAL.butter, 10);
|
|
428
|
+
c.fillStyle = K; for (let y = 70; y < 220; y += 18) for (let x = 946; x < 1196; x += 18) { c.beginPath(); c.arc(x, y, 3.4, 0, 7); c.fill(); }
|
|
429
|
+
c.restore();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// big blue circle bottom-left with stripes inside half
|
|
433
|
+
if (pop(0.58, 190, 560)) {
|
|
434
|
+
const bc = new Path2D(); bc.arc(190, 560 + bob(2.0, 5), 122, 0, Math.PI * 2);
|
|
435
|
+
solid(c, bc, PAL.blue, 12);
|
|
436
|
+
c.save(); c.clip(bc); c.fillStyle = PAL.cream; for (let x = 60; x < 330; x += 22) c.fillRect(x, 560, 11, 200); c.restore();
|
|
437
|
+
c.lineWidth = 5; c.strokeStyle = K; c.stroke(bc);
|
|
438
|
+
c.restore();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// teal triangle bottom-right
|
|
442
|
+
if (pop(0.72, 1090, 560)) {
|
|
443
|
+
c.translate(1090, 560); c.rotate(-0.2 + Math.sin(tq * 1.3) * 0.05);
|
|
444
|
+
const tri = new Path2D(); tri.moveTo(0, -120); tri.lineTo(115, 85); tri.lineTo(-115, 85); tri.closePath();
|
|
445
|
+
solid(c, tri, PAL.teal, 12);
|
|
446
|
+
c.save(); c.clip(tri); c.fillStyle = K; for (let k = -6; k < 8; k++) { c.beginPath(); c.arc(k * 26, 40, 4, 0, 7); c.fill(); c.beginPath(); c.arc(k * 26 + 13, 64, 4, 0, 7); c.fill(); } c.restore();
|
|
447
|
+
c.restore();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// lilac slab tilted behind bubble (unfolds sideways)
|
|
451
|
+
{ const sx = H.ease.back(P(0.88, 0.25));
|
|
452
|
+
if (sx > 0) {
|
|
453
|
+
c.save(); c.translate(660, 360); c.rotate(0.09); c.scale(sx, 1);
|
|
454
|
+
const slab = new Path2D(); slab.rect(-360, -150, 700, 300);
|
|
455
|
+
solid(c, slab, PAL.lilac, 0);
|
|
456
|
+
c.save(); c.clip(slab); c.strokeStyle = 'rgba(21,21,21,0.5)'; c.lineWidth = 2;
|
|
457
|
+
for (let x = -360; x < 360; x += 30) { c.beginPath(); c.moveTo(x, -150); c.lineTo(x, 150); c.stroke(); }
|
|
458
|
+
for (let y = -150; y < 150; y += 30) { c.beginPath(); c.moveTo(-360, y); c.lineTo(360, y); c.stroke(); }
|
|
459
|
+
c.restore(); c.lineWidth = 5; c.strokeStyle = K; c.stroke(slab);
|
|
460
|
+
c.restore();
|
|
461
|
+
} }
|
|
462
|
+
|
|
463
|
+
// squiggles + zigzags draw themselves on
|
|
464
|
+
const dr = (d) => H.ease.inOut(P(d, 0.4));
|
|
465
|
+
if (dr(1.0) > 0) strokeOutlined(c, squigglePath(400, 92, 300, 18, 3.5, -0.08, tq * 3, dr(1.0)), PAL.yellow, 14, 4, 7);
|
|
466
|
+
if (dr(1.1) > 0) strokeOutlined(c, zigzagPath(940, 290, 230, 18, 9, 0.12, dr(1.1)), PAL.magenta, 13, 4, 7);
|
|
467
|
+
c.lineCap = 'round'; c.strokeStyle = K; c.lineWidth = 7;
|
|
468
|
+
if (dr(1.2) > 0) c.stroke(squigglePath(610, 668, 200, 12, 4, -0.08, -tq * 3, dr(1.2)));
|
|
469
|
+
if (dr(1.05) > 0) c.stroke(squigglePath(70, 250, 140, 12, 2.5, 1.35, tq * 2.5, dr(1.05)));
|
|
470
|
+
if (dr(1.25) > 0) strokeOutlined(c, zigzagPath(330, 640, 160, 14, 7, -0.05, dr(1.25)), PAL.blue, 10, 3.5, 6);
|
|
471
|
+
|
|
472
|
+
// confetti: bursts out of the bubble, then tumbles in place
|
|
473
|
+
const rc = H.rng(77);
|
|
474
|
+
const keep = (x, y) => !(x > 250 && x < 1060 && y > 170 && y < 560);
|
|
475
|
+
const cols = [PAL.magenta, PAL.blue, PAL.yellow, PAL.teal, PAL.red, K];
|
|
476
|
+
let placed = 0;
|
|
477
|
+
for (let i = 0; i < 200 && placed < 34; i++) {
|
|
478
|
+
const x = rc.range(40, W - 40), y = rc.range(30, HH - 30); if (!keep(x, y)) continue;
|
|
479
|
+
if (Math.hypot(x - 190, y - 560) < 150 || (x > 910 && x < 1220 && y > 40 && y < 240) || Math.hypot(x - 210, y) < 210 || Math.hypot(x - 1090, y - 570) < 150) continue;
|
|
480
|
+
placed++;
|
|
481
|
+
const s = rc.range(10, 18), kind = rc.int(0, 3), col = rc.pick(cols), rot = rc.range(0, 7) + tq * rc.range(-1.5, 1.5), dl = rc.range(0, 0.15);
|
|
482
|
+
const fp = H.ease.out(P(1.4 + dl, 0.4)); if (fp <= 0) continue;
|
|
483
|
+
const px = H.lerp(640, x, fp), py = H.lerp(350, y, fp) - Math.sin(fp * Math.PI) * 60;
|
|
484
|
+
c.save(); c.translate(px, py + bob(i, 3)); c.rotate(rot + (1 - fp) * 6);
|
|
485
|
+
const p = new Path2D();
|
|
486
|
+
if (kind <= 1) { p.moveTo(0, -s); p.lineTo(s * 0.9, s * 0.6); p.lineTo(-s * 0.9, s * 0.6); p.closePath(); c.fillStyle = col; c.fill(p); c.lineWidth = 3; c.strokeStyle = K; c.stroke(p); }
|
|
487
|
+
else if (kind === 2) { c.fillStyle = col; c.fillRect(-s * 0.9, -3.5, s * 1.8, 7); }
|
|
488
|
+
else { c.beginPath(); c.arc(0, 0, s * 0.45, 0, 7); c.fillStyle = col; c.fill(); c.lineWidth = 3; c.strokeStyle = K; c.stroke(); }
|
|
489
|
+
c.restore();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ---------- hero: chunky chat bubble headline (squash & stretch pop) ----------
|
|
493
|
+
const bp = P(1.3, 0.4);
|
|
494
|
+
if (bp > 0) {
|
|
495
|
+
const e = H.ease.back(bp), breathe = bp >= 1 ? Math.sin(tq * 2.4) * 0.012 : 0;
|
|
496
|
+
const sxB = bp < 1 ? e * (1 + 0.25 * Math.sin(bp * Math.PI)) : 1 + breathe, syB = bp < 1 ? e * (1 - 0.18 * Math.sin(bp * Math.PI)) : 1 - breathe;
|
|
497
|
+
c.save(); c.translate(640, 350); c.rotate(-0.06); c.scale(sxB, syB);
|
|
498
|
+
const bw = 640, bh = 250, rr = 46;
|
|
499
|
+
const bub = new Path2D();
|
|
500
|
+
bub.moveTo(-bw / 2 + rr, -bh / 2); bub.lineTo(bw / 2 - rr, -bh / 2); bub.quadraticCurveTo(bw / 2, -bh / 2, bw / 2, -bh / 2 + rr);
|
|
501
|
+
bub.lineTo(bw / 2, bh / 2 - rr); bub.quadraticCurveTo(bw / 2, bh / 2, bw / 2 - rr, bh / 2);
|
|
502
|
+
bub.lineTo(-150, bh / 2); bub.lineTo(-230, bh / 2 + 80); bub.lineTo(-215, bh / 2);
|
|
503
|
+
bub.lineTo(-bw / 2 + rr, bh / 2); bub.quadraticCurveTo(-bw / 2, bh / 2, -bw / 2, bh / 2 - rr);
|
|
504
|
+
bub.lineTo(-bw / 2, -bh / 2 + rr); bub.quadraticCurveTo(-bw / 2, -bh / 2, -bw / 2 + rr, -bh / 2); bub.closePath();
|
|
505
|
+
solid(c, bub, PAL.yellow, 16, 6);
|
|
506
|
+
// headline: chunky with hard shadow, each word pops on its own beat
|
|
507
|
+
c.textAlign = 'center'; c.textBaseline = 'alphabetic'; c.font = '400 92px "Rubik Mono One"'; c.letterSpacing = '-7px';
|
|
508
|
+
const line = (txt, a, b, d, e2) => {
|
|
509
|
+
let x = 0, y = a, col = b; if (e2) { x = a; y = b; col = d; }
|
|
510
|
+
c.lineJoin = 'round';
|
|
511
|
+
c.fillStyle = K; c.fillText(txt, x + 7, y + 7);
|
|
512
|
+
c.lineWidth = 8; c.strokeStyle = K; c.strokeText(txt, x, y);
|
|
513
|
+
c.fillStyle = col; c.fillText(txt, x, y);
|
|
514
|
+
};
|
|
515
|
+
if (pop(1.6, 0, -40, 0.2)) { line('HELLO', -12, PAL.magenta); c.restore(); }
|
|
516
|
+
{ const wH = c.measureText('HUMAN').width, wX = c.measureText('!').width * 0.45, x0 = -(wH + wX) / 2;
|
|
517
|
+
if (pop(1.75, 0, 60, 0.2)) { c.textAlign = 'left'; line('HUMAN', x0, 88, PAL.blue, true); c.restore(); }
|
|
518
|
+
const bx = x0 + wH - c.measureText('!').width * 0.32;
|
|
519
|
+
if (pop(1.92, bx + 30, 50, 0.2)) { c.textAlign = 'left'; c.translate(bx, 0); c.rotate(0.12); line('!', 0, 88, PAL.magenta, true); c.restore(); }
|
|
520
|
+
c.textAlign = 'center'; }
|
|
521
|
+
c.letterSpacing = '0px';
|
|
522
|
+
// typing dots badge
|
|
523
|
+
if (pop(2.05, 250, -118, 0.2)) {
|
|
524
|
+
c.translate(250, -118); c.rotate(0.1);
|
|
525
|
+
const bd = new Path2D(); bd.ellipse(0, 0, 62, 30, 0, 0, Math.PI * 2);
|
|
526
|
+
solid(c, bd, PAL.cream, 6, 4);
|
|
527
|
+
for (let k = 0; k < 3; k++) { const up = Math.max(0, Math.sin(tq * 6 - k * 0.9)) * 6; c.fillStyle = [PAL.magenta, PAL.teal, PAL.blue][k]; c.beginPath(); c.arc(-24 + k * 24, -up, 8, 0, 7); c.fill(); c.lineWidth = 3; c.strokeStyle = K; c.stroke(); }
|
|
528
|
+
c.restore();
|
|
529
|
+
}
|
|
530
|
+
c.restore();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// small caption sticker slapped on last
|
|
534
|
+
{ const sp = P(2.2, 0.2);
|
|
535
|
+
if (sp > 0) {
|
|
536
|
+
const k = H.ease.out(sp);
|
|
537
|
+
c.save(); c.translate(860, 548); c.rotate(-0.04 - (1 - k) * 0.35); c.scale(1.35 - 0.35 * k, 1.35 - 0.35 * k);
|
|
538
|
+
const st = new Path2D(); st.rect(-120, -24, 240, 48);
|
|
539
|
+
solid(c, st, PAL.cream, 7 * k, 4);
|
|
540
|
+
c.fillStyle = K; c.font = '700 26px "Fredoka"'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.fillText('your new AI pal', 0, 1);
|
|
541
|
+
c.restore();
|
|
542
|
+
} }
|
|
543
|
+
|
|
544
|
+
// ---------- print finish ----------
|
|
545
|
+
H.grain(ctx, W, HH, 0.05, 6 + Math.floor(t * 12), true);
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 67,
|
|
3
|
+
"name": "Memphis Group",
|
|
4
|
+
"nameZh": "孟菲斯设计",
|
|
5
|
+
"family": "Art & Design Movements",
|
|
6
|
+
"familyZh": "艺术与设计流派",
|
|
7
|
+
"description": "Early-80s Memphis Milano maximalism: pastel grounds clashing with bold primaries, thick black outlines and hard offset shadows, squiggles, zigzags, dot grids, terrazzo chips and confetti triangles, all orbiting a chunky tilted chat-bubble headline.",
|
|
8
|
+
"motionNotes": "Motion: Cut-out cel timing on twos: the mint wedge slides in, shapes pop on one by one with springy overshoot, squiggles and zigzags draw themselves, the chat bubble squash-and-stretches in, the words pop on the beat, confetti bursts out and the sticker slaps on; then everything bobs off-beat and the typing dots bounce.",
|
|
9
|
+
"explainerNotes": "Explainer: Memphis cut-out animation on twos: examples hop into a funnel-topped totem computer, scenes change with a striped polka-dot band that swipes across, arrows are fat outlined shapes with hard shadows, counters are stickers that pop on each tick, and the key moments get a spinning starburst.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "memphis",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|