{"_id":"@danhnm1203/scrollytelling","_rev":"10-bf0390cd461792e3459fd9c4a742f70f","name":"@danhnm1203/scrollytelling","dist-tags":{"latest":"0.7.0"},"versions":{"0.1.0":{"name":"@danhnm1203/scrollytelling","version":"0.1.0","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.1.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"34ea9c15ae22f4a013a0e2b67c8b3c5a70b870b8","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.1.0.tgz","fileCount":28,"integrity":"sha512-Nc6dR6iBnzHjyL6kF0vPfemr+bNashurfW4fu3eFskJt92iGF0Ntk0A5cd4SF71Dn2YJ1bgr3AA37Sj6HNzvuA==","signatures":[{"sig":"MEUCIQDuYSDAaNeKQny4+a410JSM/6QJoSlFGWDka0x8dMSNVwIgGdyIuC4Y1CWla1F+VEfvQyEnO5lwFTxCWrrASX3UXVo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138026},"type":"module","engines":{"node":">=20"},"gitHead":"d8ee0f08d060d9684b3a438f57a00b0929391e18","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.0","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.12.0","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.1.0_1785863334556_0.5089167058105553","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@danhnm1203/scrollytelling","version":"0.2.0","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.2.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"07edacfa68d4267d7c99f0c3753cf0c41c4971f7","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.2.0.tgz","fileCount":31,"integrity":"sha512-yIDeyNGNOlYw3Yu0M8lgAGWNrwRpPmNjGfD5KCf2fcHMtor7jiI+nfgTR0On8fG8RiiV2E8N9gJY0F3FcStxiw==","signatures":[{"sig":"MEYCIQCF9rM2Qfczy30quxw+lyzIl3R/PBa7DZTpYswtjWMWTgIhAOHnIs2yuYWxN2Ow0ejIHQFYrsp4j/j2OonuRfHOl7Kj","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":162202},"type":"module","engines":{"node":">=20"},"gitHead":"504a333f54d3e110edd5b064e1f2e6c3445982c8","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.2.0_1785987343629_0.8163386111157482","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@danhnm1203/scrollytelling","version":"0.3.0","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.3.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"593d65e930c790d41ba920809a2906e5324f9d85","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.3.0.tgz","fileCount":54,"integrity":"sha512-IhgrfLcvHl2jiDdIXBhSyVTflKYZcqUrHb5vJJUaT8rOc3hgqpzGN7mhy+WXwM0ytXq+uf4eESuK+wMYQxu5Mg==","signatures":[{"sig":"MEUCIQDBGXKLs388yUB+kHARiCJP3SZ5M7DCYZyHyaOYksZyzAIgNUY7DNA2d7xgyvBx4u0s2j3UA1COwk3tf9ERfvxZ7/s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":218686},"type":"module","engines":{"node":">=20"},"gitHead":"02d226bb5b855c89d54b5dd0ddc32a15bc7fbd91","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.3.0_1785997950029_0.5396847180357964","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@danhnm1203/scrollytelling","version":"0.4.0","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.4.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"8ae8ea6cf65af586a1b7f51dc3e7c6f77b9b923a","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.4.0.tgz","fileCount":63,"integrity":"sha512-c7tR9uPwnm+3fyX16YDk5Jn2sXFDIoNe2i2YIyIZX5CvGycW6UrZOX23LTpwZwI6qHFkDzbYW6r1znGUKY4NfQ==","signatures":[{"sig":"MEQCIDhSM1LFnGe/VgH9IW7pu2pgCDOnBNosInGZTIq4ApQjAiARaqeb0dBVyJEfjvZEgWvpsegpl9h0OhLLYvuwdbQOfg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":233153},"type":"module","engines":{"node":">=20"},"gitHead":"ad00d557243f9c5a85ac5f8f22a6d87f38b77cab","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.4.0_1786003319597_0.28076216981756197","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@danhnm1203/scrollytelling","version":"0.4.1","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.4.1","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"122fdf3cd22de60eff5be7787fe29aada02b4402","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.4.1.tgz","fileCount":63,"integrity":"sha512-EmqzNUmYMZEJBc+PivGbJ4Z1pHReN1Ovk4390Y/hqGFkzb4n46ne1RNhqL6zHcPxik5bepkCB419tbgNQnUeSA==","signatures":[{"sig":"MEUCIQDvu3zyhvZhloOAWAYWAZKUE7DAU/ywuYPqceOlPyXHVAIgaTTIt+TEwtd5KvwGxpeH9PSlnnFGALatgzbryQnphc4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":248283},"type":"module","engines":{"node":">=20"},"gitHead":"59b33377f14f1c3d46abda46d4bd77ad8747e13e","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.4.1_1786005559117_0.3629192906029497","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@danhnm1203/scrollytelling","version":"0.4.2","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.4.2","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"8ef58f60bf17484b4ece04d7143748811e3a41ac","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.4.2.tgz","fileCount":63,"integrity":"sha512-y2AHPq+uRjs/r5RE78/UNI6kwvT2v+oBidJy7G7fKGu031T0I/9bKxMoKox33xmFrbmrCVlW59p+Fm/FcGm0Qg==","signatures":[{"sig":"MEQCIFrkDeCRxQEYsqQgIv+6gPyJ0lh14+AHajWFQvAM60mNAiBUXyImWU0VtR73xNB7SsHP/GuvTKDab8L3+Zz/FsBX0A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":250382},"type":"module","engines":{"node":">=20"},"gitHead":"72992413d72d72be870c25567170a4a479a4bf04","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.4.2_1786008525277_0.8311390933291565","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@danhnm1203/scrollytelling","version":"0.5.0","keywords":["scrollytelling","scroll-animation","image-sequence","canvas","nextjs","video"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.5.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"78ae6ad12f2b603086b58efd9f0137778a320b28","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.5.0.tgz","fileCount":63,"integrity":"sha512-g7f0xPum8G0dKe667GEMTSHjMs2LiZTzKVU/MQwAzDEccSdS1anvqLMC1dfSu14XYEnWdQPwhO+WWLdtCe3zBg==","signatures":[{"sig":"MEQCIHl/vy5xua9dYdtKbAXEB+XnSE3W44PTmkW9kJIft4ICAiAVtLZacWfig8r5uu4NeapJgumj02K4moGjQsJaT7MJfw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":276308},"type":"module","engines":{"node":">=20"},"gitHead":"a1da5b7d5fab2bf7e3a9463ac91aed9a46d20866","scripts":{"test":"node --test"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video into a scroll-scrubbed Next.js landing page. The clip plays frame by frame as the visitor scrolls.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.5.0_1786066895339_0.0933336137969838","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@danhnm1203/scrollytelling","version":"0.6.0","keywords":["scrollytelling","landing-page","hero-section","image-sequence","video","nextjs","nuxt","astro","html","ffmpeg","claude-code"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.6.0","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://github.com/danhnm1203/scrollytelling#readme","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"f452e8a2dff9b0ba4e7613088f12460f32ac0c1c","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.6.0.tgz","fileCount":63,"integrity":"sha512-RrK/vgfzd8rIT0dGMh+y9UAva3s4eUfXrz0SnRWVyME79H1955Y0JiVFpIhap4Dcy7w3XlPgzbj9qYe/Xv1cEg==","signatures":[{"sig":"MEYCIQDdPx89p6Y1cDYM/X00FNtgT545p1ZTCxUQ8PLZ0Oi7wwIhAKB3rMRWYQX5ugsoTSSKK5LsNPFtWh6U8stqS11/PlTC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":281341},"type":"module","engines":{"node":">=20"},"gitHead":"26aa172f89a48ed219a308dda723175061d0b5fb","scripts":{"test":"node --test","sample":"node tools/sample-site.mjs"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video you already have into a complete scroll-scrubbed hero page, in one command. Next, Nuxt, Astro or plain HTML.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.6.0_1786077933420_0.31629506301811183","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"@danhnm1203/scrollytelling","version":"0.6.1","keywords":["scrollytelling","landing-page","hero-section","image-sequence","video","nextjs","nuxt","astro","html","ffmpeg","claude-code"],"license":"MIT","_id":"@danhnm1203/scrollytelling@0.6.1","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"homepage":"https://danhnm1203.github.io/scrollytelling/","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"bin":{"scrollytelling":"bin/cli.mjs"},"dist":{"shasum":"d702735426e133c6b0c45dce4dc2dafb88359100","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.6.1.tgz","fileCount":63,"integrity":"sha512-uDENRVhIzLVO7oXRBvF2WwCIvsxqmkPqX4Yii2syaSfeRiTmO3w1qIbmhh3B7JQmJPjFmvx/ES6hvhio7RMVpA==","signatures":[{"sig":"MEYCIQC1naI9IcUjrpAiQbHk1BgFVjsDicSityft4Cdv5+3kRQIhAIffNXE+5BEZc/vl4dx1gAlTei0OISChlQWAIH2CFbHM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.6.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":283843},"type":"module","engines":{"node":">=20"},"gitHead":"198431793c15c1d44611c04bc9f8eb7bdb08d470","scripts":{"test":"node --test","sample":"node tools/sample-site.mjs"},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"repository":{"url":"git+https://github.com/danhnm1203/scrollytelling.git","type":"git"},"_npmVersion":"10.9.8","description":"Turn a video you already have into a complete scroll-scrubbed hero page, in one command. Next, Nuxt, Astro or plain HTML.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"sharp":"0.35.3","ffmpeg-static":"5.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scrollytelling_0.6.1_1786079708726_0.22220728443822102","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@danhnm1203/scrollytelling","version":"0.7.0","description":"Turn a video you already have into a complete scroll-scrubbed hero page, in one command. Next, Nuxt, Astro or plain HTML.","homepage":"https://danhnm1203.github.io/scrollytelling/","license":"MIT","type":"module","repository":{"type":"git","url":"git+https://github.com/danhnm1203/scrollytelling.git"},"bin":{"scrollytelling":"bin/cli.mjs"},"engines":{"node":">=20"},"scripts":{"test":"node --test","sample":"node tools/sample-site.mjs"},"dependencies":{"ffmpeg-static":"5.3.0","sharp":"0.35.3"},"keywords":["scrollytelling","landing-page","hero-section","image-sequence","video","nextjs","nuxt","astro","html","ffmpeg","claude-code"],"_id":"@danhnm1203/scrollytelling@0.7.0","gitHead":"20b2c33f88804fcddf559e6a222c36c20ba1ef78","bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-zPCWdUy424FBBkabowU2EOJKMsHE0Wige1CbU8IAKZwgsXFzBbHh0wI6BbwiDBwwbikN/HBzcdw5axR2aE3dTw==","shasum":"83184b2de475920c7b49286fd0d0a002a18b1888","tarball":"https://registry.npmjs.org/@danhnm1203/scrollytelling/-/scrollytelling-0.7.0.tgz","fileCount":65,"unpackedSize":315035,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@danhnm1203%2fscrollytelling@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICylimn38Dy/+8qnbSmkFBX7nB3gDB8Z6XrSLX5ca4mOAiEA5sz1E49pkZQ9/eRUJJdS57rIkJMP3m4bgmx7W+vzLUI="}]},"_npmUser":{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"},"directories":{},"maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scrollytelling_0.7.0_1786113200608_0.8637393670794238"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T17:08:54.304Z","modified":"2026-08-07T14:33:21.108Z","0.1.0":"2026-08-04T17:08:54.716Z","0.2.0":"2026-08-06T03:35:43.783Z","0.3.0":"2026-08-06T06:32:30.157Z","0.4.0":"2026-08-06T08:01:59.753Z","0.4.1":"2026-08-06T08:39:19.266Z","0.4.2":"2026-08-06T09:28:45.419Z","0.5.0":"2026-08-07T01:41:35.491Z","0.6.0":"2026-08-07T04:45:33.576Z","0.6.1":"2026-08-07T05:15:08.858Z","0.7.0":"2026-08-07T14:33:20.774Z"},"bugs":{"url":"https://github.com/danhnm1203/scrollytelling/issues"},"license":"MIT","homepage":"https://danhnm1203.github.io/scrollytelling/","keywords":["scrollytelling","landing-page","hero-section","image-sequence","video","nextjs","nuxt","astro","html","ffmpeg","claude-code"],"repository":{"type":"git","url":"git+https://github.com/danhnm1203/scrollytelling.git"},"description":"Turn a video you already have into a complete scroll-scrubbed hero page, in one command. Next, Nuxt, Astro or plain HTML.","maintainers":[{"name":"danhnm1203","email":"danhnguyen1998@gmail.com"}],"readme":"<div align=\"center\">\n\n# scrollytelling\n\n**Turn a video you already have into a complete hero page, in one command.**\n\n[<img src=\"docs/media/scroll.gif\" alt=\"One command scaffolds a project and encodes a clip, printing how bright the footage is by region and scroll position. The page it produces is then scrolled: the clip advances frame by frame while copy fades in over it, and the page continues below the scroll.\" width=\"800\">](https://danhnm1203.github.io/scrollytelling/)\n\nThe clip advances frame by frame as the visitor scrolls, with your copy fading in\nover it. Every frame is measured as it is encoded, so the text stays readable and\nthe page has no visible seams — on your footage, not just on a showreel.\n\n**Next · Nuxt · Astro · plain HTML.** No accounts, no API keys, no runtime\ndependency beyond the framework you already chose.\n\n```bash\nnpx @danhnm1203/scrollytelling scaffold ./my-site\n```\n\n**[Scroll the demo →](https://danhnm1203.github.io/scrollytelling/)** — built by this tool, from a video, in one command.\n\n[![test](https://github.com/danhnm1203/scrollytelling/actions/workflows/test.yml/badge.svg)](https://github.com/danhnm1203/scrollytelling/actions/workflows/test.yml)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-3c873a)](https://nodejs.org)\n[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)\n\n</div>\n\n---\n\n## Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Quickstart with a coding agent](#quickstart-with-a-coding-agent)\n- [Quickstart with the CLI](#quickstart-with-the-cli)\n- [Why it looks right](#why-it-looks-right)\n- [Reduced motion](#reduced-motion)\n- [What you get](#what-you-get)\n- [License](#license)\n\nLonger reference material lives beside this file:\n[CLI reference](docs/en/cli-reference.md) ·\n[Templates](docs/en/templates.md) ·\n[Why it looks right, in full](docs/en/why-it-looks-right.md) ·\n[Frames are committed to git](docs/en/frames-in-git.md) ·\n[Development and contributing](docs/en/contributing.md)\n\n## Requirements\n\nNode 20 or newer. Nothing else: `sharp` and `ffmpeg-static` come with this\npackage and bring their own binaries.\n\n## Installation\n\nThe command at the top of this file runs without installing anything — the first\nrun takes a minute, then it is cached. If you will use it more than once:\n\n```bash\nnpm i -g @danhnm1203/scrollytelling\n```\n\nEither form gives you a `scrollytelling` command.\n\n### Using it from a coding agent\n\nClaude Code, as a plugin:\n\n```\n/plugin marketplace add danhnm1203/scrollytelling\n/plugin install scrollytelling@scrollytelling\n```\n\nCodex and other agents:\n\n```bash\nnpx skills add danhnm1203/scrollytelling\n```\n\nOr clone and copy `skills/scrollytelling` into `~/.claude/skills/`. Either way,\nstart a new session and call it as `/scrollytelling`.\n\n## Quickstart with a coding agent\n\nOnce the skill is installed, hand it the footage and say what the page is for:\n\n```\n/scrollytelling <video-or-image-dir> [project-dir] [what the page is about]\n```\n\nThe path is the only part that has to be there. Everything else is a prompt —\nsay as much or as little as you want about the story:\n\n```\n/scrollytelling ./watch-teardown.mp4 ./watch-site\n```\n\n```\n/scrollytelling turn ./drone-flyover.mp4 into a landing page for a vineyard\ntour. Three beats, calm and unhurried, and keep the copy off the horizon.\n```\n\n```\n/scrollytelling ./renders/ is an ordered image sequence of our headphones\nrotating. Build it into ./hp-page — two beats only, and the subject sits\nleft of centre rather than middle.\n```\n\nThe agent runs the same pipeline described below, and does the two parts that\nneed judgement rather than a flag:\n\n- **It looks at the footage.** It extracts preview frames, reads them, and writes\n  your beats against what is actually on screen instead of dropping them on\n  `0.3 / 0.6 / 0.9`.\n- **It checks its own work in a browser.** It builds the page, scrubs it at\n  1440×900 and 375×812, screenshots each beat and judges the contrast, and\n  confirms the canvas really changes when scrolling back up.\n\nIt will tell you what it chose and why. The project it leaves behind is a normal\nproject in whichever stack you asked for — see [What you get](#what-you-get).\n\n## Quickstart with the CLI\n\nFive steps, start to finish. The order matters: the footage decides the story,\nnot the other way round, so you look at the clip before writing a word of copy.\n\n### 1. Look at the footage\n\n```bash\nscrollytelling frames --preview ./clip.mp4\n```\n\nWrites five stills, evenly spaced across the clip, to a temporary directory and\nprints the path. Open them. You are looking for where the frame is busy, where it\nis dark enough to carry white text, and what the clip is about at each point in\nits run.\n\nNothing is created in your project yet — this step is free, and you can repeat it\non different clips until one is right.\n\n### 2. Create the project\n\n```bash\nscrollytelling scaffold ./my-site\ncd my-site\nnpm install\n```\n\nYou now have a normal Next.js project — or another stack, see below. `scaffold`\nnever overwrites a file you\nhave edited, so it is safe to re-run later — see\n[Keeping up with template fixes](#keeping-up-with-template-fixes).\n\n#### Other templates\n\nNext is the default. Three others ship, and all four run the same engine:\n\n```bash\nscrollytelling scaffold ./my-site --template nuxt\nscrollytelling scaffold ./my-site --template astro\nscrollytelling scaffold ./my-site --template html\n```\n\nWhat each one gives you, and the two things worth knowing about `html`, are in\n[Templates](docs/en/templates.md).\n\n### 3. Turn the clip into a measured frame sequence\n\n```bash\nscrollytelling frames ../clip.mp4 . --frames 50\n```\n\nThis extracts, measures and encodes. It writes the images to `public/frames/` and\nthe contract they are described by to `components/frames.js`, and prints a table\nlike this:\n\n```\nWrote 100 frames across landscape 1280x720, portrait 720x1280 (4.31 MB)\n\nlandscape — how bright the footage is, by region and scroll position\n           0%  17%  33%  50%  67%  83%\n  left   0.12 0.18 0.44 0.71 0.68 0.55\n  centre 0.31 0.29 0.35 0.52 0.49 0.44\n  right  0.09 0.11 0.14 0.22 0.61 0.58\n  Copy over left and right will need a heavy scrim somewhere in the scroll.\n```\n\nRead it as: how bright each third of the frame is, at each point in the scroll.\n`0.0` is black, `1.0` is white. Above about `0.55` — the threshold `--check` uses\nin the next step — is a bad place to put white text.\n\nStart with 50 frames. Raise it if the scrub feels choppy or the report warns that\nthe background pulses between frames; lower it if `public/frames/` gets heavy.\n\n### 4. Write your beats, then check them\n\nOpen `components/story.js` — the only file you need to edit. Each beat declares\n`at`, the scroll position from 0 to 1 where it should be clearest:\n\n```js\nsections: [\n  { at: 0.0,  align: \"center\", heading: \"Orbit\", body: \"Scroll to take it apart.\" },\n  { at: 0.3,  align: \"left\",   heading: \"Nothing wasted\", body: \"…\" },\n  { at: 0.6,  align: \"right\",  heading: \"Built to be understood\", body: \"…\" },\n]\n```\n\nPlace them against the table from step 3. The `left` beat above sits at `0.3`,\nwhere the left third reads `0.44` — comfortable. Move it to `0.6` and it lands on\n`0.71`, which is too bright to read against. Then check your work:\n\n```bash\nscrollytelling frames --check .\n```\n\nIt reads the beats you wrote, compares each against the frames that will actually\nbe behind it, and names the ones that will be hard to read and where to move\nthem. Adjust `at` or `align` and run it again — it re-reads the generated\ncontract, so it costs nothing.\n\n### 5. Run it\n\n```bash\nnpm run dev\n```\n\nScroll the page. Check it at phone width too — the portrait sequence built in\nstep 3 is what you will see there.\n\n### Putting the rest of your page under it\n\nThe hero is not the whole page unless you want it to be. Add sections below the\nrunway — the element marked `data-scrollytelling-runway` — and the sequence still\nruns from the first frame to the last inside it: progress is measured against\nthat element, not against the document. The copy overlays and the progress bar\nbelong to the hero too, so they end where it ends rather than floating over\nwhatever comes next.\n\nTwo things follow from that. The runway is what decides how much scrolling the\nsequence gets, so lengthen or shorten it there rather than anywhere else. And if\nyou write your own markup, keep the attribute and keep the hero inside it a\nviewport tall, stuck to the top — that is the travel the scrub is derived from.\nWithout the attribute the page falls back to measuring the document, which is\nonly the same thing when the hero is all there is; the engine says so in the\nconsole when it happens.\n\n## Why it looks right\n\nTwo measurements are taken while each frame is encoded, and the page uses both.\n\n**The page background matches each frame's own border color**, interpolated as\nyou scroll, so the canvas has no visible edge against the page. Pin the page to a\nsingle hex instead and that edge shows on every frame whose border does not\nhappen to match — which is every frame, on any footage with a gradient or\nchanging exposure.\n\n**Each block of copy gets a backdrop sized to the luminance behind it.** White\ntext at a fixed opacity stops being readable the moment a frame brightens under\nit. Over dark footage the backdrop is nearly invisible and the image stays clean.\n\nIt also builds **two sequences**, landscape and a portrait crop, and eases toward\nthe scroll position rather than snapping to it — which is the difference between\nmotion and a slideshow.\n\n[The whole of it, and why each choice is the way it is →](docs/en/why-it-looks-right.md)\n\n## Reduced motion\n\nScrubbing a sequence is motion driven by interaction, so a visitor whose system\nasks for less of it does not get a slower version — they get a different page:\none still, and the story as prose. No worker starts, nothing is decoded, and\nthere is no runway to scroll past.\n\nThat page costs nothing to produce because it already exists. The generated\nproject carries the whole story as an ordinary document for screen readers and\ncrawlers, since four beats fading in and out of a canvas read as disconnected\nfragments to anything that cannot see them. Under reduced motion that document\nstops being screen-reader-only and becomes the page itself — the same copy, in\nthe same order, that everyone else scrolls through.\n\nThe practical consequence: what you write in `components/story.js` is the page\ntwice over. It is worth reading once as flat prose before you ship.\n\n## CLI reference\n\n```\nscrollytelling scaffold <project_dir> [--force] [--diff]\nscrollytelling frames <video|image-dir> <project_dir> [options]\nscrollytelling frames --preview <video>\nscrollytelling frames --check <project_dir>\n```\n\n### `frames` options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `--frames <n>` | `50` | Frames in the sequence |\n| `--max-width <px>` | `1280` | Longest edge of the encoded webp |\n| `--quality <n>` | `82` | webp quality |\n| `--focus <0-1>` | `0.5` | Where the portrait crop sits horizontally |\n| `--skip-portrait` | off | Build only the landscape sequence |\n\nInput can be a video or a directory of ordered stills.\n\n### `scaffold` options\n\n| Option | Description |\n| --- | --- |\n| `--force` | Overwrite files you have edited |\n| `--diff` | Report template changes since you scaffolded — see below |\n\n## What you get\n\nA normal project in whichever stack you asked for, depending on that stack and\nnothing else — the `html` template depends on nothing at all. The video and image\ntooling stays in this package, so cloning, building and deploying your page never\ninstalls it. The trade-off is that installing *this* package fetches an ffmpeg\nbinary (~80MB) once.\n\nLaid out below is the default `next` project, which depends on `next`, `react`\nand `react-dom`. The other three differ in the framework files around the edge;\n`lib/` and `public/frames/` are the same in all four — see\n[Templates](docs/en/templates.md).\n\n```\nmy-site/\n  app/                     page, layout, styles\n  components/\n    story.js               brand and copy beats — the only file you edit\n    story.d.ts             its types, so your editor still checks it\n    frames.js              generated; do not hand-edit\n    frames.d.ts            its types\n    ScrollSequence.tsx     the adapter — about sixty lines\n  lib/                     copied from this package at scaffold time\n    scroll-engine.mjs      the scrubbing engine, shared by every template\n    scroll-engine.css      its styles, themeable via custom properties\n    scroll-engine-state.mjs  the decisions it makes, pure and unit-tested\n    scroll-math.mjs        the primitives underneath\n    decoder.worker.js      off-thread frame decoding\n  public/frames/           generated, committed\n```\n\nOnce generated, the project is yours. `scaffold` never overwrites a file you have\nedited unless you pass `--force`.\n\n### Keeping up with template fixes\n\nScaffolding records what the template looked like at the time in\n`.scrollytelling-version`. Later, `scaffold <project_dir> --diff` reports what has\nmoved since:\n\n```\nChanged in the template, untouched in your project:\n  components/ScrollSequence.tsx\n  Safe to take: copy them from a fresh scaffold in a temporary directory.\n\nChanged in the template AND edited by you:\n  app/globals.css\n  Your call. Adopting these would discard your edits.\n```\n\nIt only reports. Adopting a change is your decision — the code is yours once it\nis generated, and a tool that rewrote it on your behalf would make re-running\nthis something to be afraid of.\n\nCommit `.scrollytelling-version` along with the rest of the project. It is the\nonly record of which template your project came from; without it `--diff` has no\nbaseline to compare against and can only tell you to re-scaffold.\n\n`public/frames/` is committed rather than gitignored, which is unusual enough to\nbe worth a page of its own: [Frames are committed to git](docs/en/frames-in-git.md).\n\n## Gallery\n\nPages built with this tool.\n\n<!-- Entries land here from the \"Show your page\" form. Nothing goes in without\n     the submitter having ticked the gallery checkbox on that form. -->\n\n*Nothing here yet — yours could be first.*\n\n**[Show the page you built →](https://github.com/danhnm1203/scrollytelling/issues/new?template=show-your-page.yml)**\n\nEvery entry says who built it, and pages submitted by other people are theirs:\nlisted only when the form's gallery checkbox was ticked, credited to whoever made\nthem, and **removed within 24 hours of the owner asking** — reply on your issue,\nor open a new one.\n\n## Working on this repository\n\nTests, the invariants they protect, and what to check before opening a PR:\n[Development and contributing](docs/en/contributing.md).\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}