├── .DS_Store
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── .DS_Store
├── 1.webp
├── 2.webp
├── 3.webp
├── 4.webp
└── 5.webp
├── css
├── base.css
└── demo.css
├── favicon.ico
├── index.html
├── js
├── script.js
└── utils.js
├── package.json
├── shader
├── baseFragment.glsl
├── baseVertex.glsl
├── effectFragment.glsl
├── effectVertex.glsl
└── resources
│ ├── noise.glsl
│ └── utils.glsl
├── vite.config.js
└── yarn.lock
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .cache
3 | .parcel-cache
4 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2009 - 2024 [Codrops](https://tympanus.net/codrops)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # On-Scroll Image Distortion and Grain Effect
2 |
3 | Demo for the tutorial on how to create on-scroll distortion and grain effect with shaders in Three.js.
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=78948)
8 |
9 | [Demo](http://tympanus.net/Tutorials/ShaderOnScroll/)
10 |
11 | ## Installation
12 |
13 | Install dependencies:
14 | ```
15 | yarn install
16 | ```
17 |
18 | Run local environment:
19 | ```
20 | yarn dev
21 | ```
22 |
23 | Create build:
24 | ```
25 | yarn build
26 | ```
27 |
28 | ## Credits
29 |
30 | - Images generated with [Midjourney](https://midjourney.com)
31 |
32 | ## Misc
33 |
34 | Follow Jan Kohlbach: [Twitter](https://x.com/jankohlbach), [Instagram](https://instagram.com/jankohlbach.work), [GitHub](https://github.com/jankohlbach)
35 |
36 | Follow Codrops: [X](http://www.X.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/)
37 |
38 | ## License
39 | [MIT](LICENSE)
40 |
41 | Made with 💙 by [Codrops](http://www.codrops.com) and [Jan Kohlbach](https://x.com/jankohlbach)
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/1.webp
--------------------------------------------------------------------------------
/assets/2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/2.webp
--------------------------------------------------------------------------------
/assets/3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/3.webp
--------------------------------------------------------------------------------
/assets/4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/4.webp
--------------------------------------------------------------------------------
/assets/5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/assets/5.webp
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 13px;
9 | --color-text: #232222;
10 | --color-bg: #fff;
11 | --color-link: #232222;
12 | --color-link-hover: #000;
13 | --page-padding: 1.5rem;
14 | }
15 |
16 | body {
17 | margin: 0;
18 | color: var(--color-text);
19 | background-color: var(--color-bg);
20 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif;
21 | -webkit-font-smoothing: antialiased;
22 | -moz-osx-font-smoothing: grayscale;
23 | }
24 |
25 | /* Page Loader */
26 | .js .loading::before,
27 | .js .loading::after {
28 | content: '';
29 | position: fixed;
30 | z-index: 10000;
31 | }
32 |
33 | .js .loading::before {
34 | top: 0;
35 | left: 0;
36 | width: 100%;
37 | height: 100%;
38 | background: var(--color-bg);
39 | }
40 |
41 | .js .loading::after {
42 | top: 50%;
43 | left: 50%;
44 | width: 100px;
45 | height: 1px;
46 | margin: 0 0 0 -50px;
47 | background: var(--color-link);
48 | animation: loaderAnim 1.5s ease-in-out infinite alternate forwards;
49 |
50 | }
51 |
52 | @keyframes loaderAnim {
53 | 0% {
54 | transform: scaleX(0);
55 | transform-origin: 0% 50%;
56 | }
57 | 50% {
58 | transform: scaleX(1);
59 | transform-origin: 0% 50%;
60 | }
61 | 50.1% {
62 | transform: scaleX(1);
63 | transform-origin: 100% 50%;
64 | }
65 | 100% {
66 | transform: scaleX(0);
67 | transform-origin: 100% 50%;
68 | }
69 | }
70 |
71 | a {
72 | text-decoration: none;
73 | color: var(--color-link);
74 | outline: none;
75 | cursor: pointer;
76 | }
77 |
78 | a:hover {
79 | text-decoration: underline;
80 | color: var(--color-link-hover);
81 | outline: none;
82 | }
83 |
84 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */
85 | a:focus {
86 | /* Provide a fallback style for browsers
87 | that don't support :focus-visible */
88 | outline: none;
89 | background: lightgrey;
90 | }
91 |
92 | a:focus:not(:focus-visible) {
93 | /* Remove the focus indicator on mouse-focus for browsers
94 | that do support :focus-visible */
95 | background: transparent;
96 | }
97 |
98 | a:focus-visible {
99 | /* Draw a very noticeable focus style for
100 | keyboard-focus on browsers that do support
101 | :focus-visible */
102 | outline: 2px solid red;
103 | background: transparent;
104 | }
105 |
106 | .unbutton {
107 | background: none;
108 | border: 0;
109 | padding: 0;
110 | margin: 0;
111 | font: inherit;
112 | cursor: pointer;
113 | }
114 |
115 | .unbutton:focus {
116 | outline: none;
117 | }
118 |
119 | .frame {
120 | padding: var(--page-padding);
121 | position: relative;
122 | display: grid;
123 | z-index: 1000;
124 | width: 100%;
125 | height: 100%;
126 | grid-row-gap: 1rem;
127 | grid-column-gap: 2rem;
128 | pointer-events: none;
129 | justify-items: start;
130 | grid-template-columns: auto auto;
131 | grid-template-areas: 'title' 'archive' 'back' 'github' 'sponsor' 'demos' 'tags';
132 | }
133 |
134 | .frame #cdawrap {
135 | justify-self: start;
136 | }
137 |
138 | .frame a {
139 | pointer-events: auto;
140 | }
141 |
142 | .frame__title {
143 | grid-area: title;
144 | font-size: inherit;
145 | font-weight: 700;
146 | margin: 0;
147 | }
148 |
149 | .frame__back {
150 | grid-area: back;
151 | justify-self: start;
152 | }
153 |
154 | .frame__archive {
155 | grid-area: archive;
156 | justify-self: start;
157 | }
158 |
159 | .frame__sub {
160 | grid-area: sub;
161 | }
162 |
163 | .frame__github {
164 | grid-area: github;
165 | }
166 |
167 | .frame__tags {
168 | grid-area: tags;
169 | display: flex;
170 | gap: 1rem;
171 | }
172 |
173 | .frame__hire {
174 | grid-area: hire;
175 | }
176 |
177 | .frame__demos {
178 | grid-area: demos;
179 | display: flex;
180 | gap: 1rem;
181 | }
182 |
183 | .content {
184 | padding: var(--page-padding);
185 | display: flex;
186 | flex-direction: column;
187 | position: relative;
188 | }
189 |
190 | @media screen and (min-width: 53em) {
191 | body {
192 | --page-padding: 1rem;
193 | }
194 | .frame {
195 | position: fixed;
196 | top: 0;
197 | left: 0;
198 | width: 100%;
199 | height: 100%;
200 | grid-template-columns: auto auto 1fr auto 1fr;
201 | grid-template-rows: auto auto;
202 | align-content: space-between;
203 | grid-template-areas: 'title title ... back sponsor' 'tags tags ... github archive';
204 | }
205 | .frame #cdawrap {
206 | justify-self: end;
207 | max-width: 250px;
208 | text-align: right;
209 | }
210 | .content {
211 | min-height: 100vh;
212 | justify-content: center;
213 | align-items: center;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/css/demo.css:
--------------------------------------------------------------------------------
1 | /* vars */
2 | :root {
3 | --fluid-min: 375;
4 | --fluid-max: 1720;
5 | --container-max-width: 120rem;
6 | --grid-gutter: clamp(0.625rem, -0.2463rem + 3.7175vw, 3.75rem);
7 | --grid-columns: 6;
8 | }
9 |
10 | /* scroll */
11 | .lenis.lenis-smooth {
12 | scroll-behavior: auto;
13 | }
14 |
15 | .lenis.lenis-smooth [data-lenis-prevent] {
16 | overscroll-behavior: contain;
17 | }
18 |
19 | .lenis.lenis-stopped {
20 | overflow: hidden;
21 | }
22 |
23 | /* grid */
24 | .container {
25 | max-width: var(--container-max-width);
26 | margin-inline: auto;
27 | }
28 |
29 | .grid {
30 | display: grid;
31 | gap: calc(var(--grid-gutter) * 5) var(--grid-gutter);
32 | grid-template-columns: repeat(var(--grid-columns), 1fr);
33 | padding-block: 6rem;
34 | }
35 |
36 | /* webgl */
37 | [data-webgl-media] {
38 | opacity: 0;
39 | }
40 |
41 | /* demo */
42 | canvas {
43 | position: fixed;
44 | inset: 0;
45 | z-index: 100;
46 | width: 100%;
47 | height: 100%;
48 | opacity: 1;
49 | pointer-events: none;
50 | }
51 |
52 | .img-wrap {
53 | display: grid;
54 | gap: 1rem;
55 | grid-template-columns: repeat(3, 1fr);
56 | max-width: 100%;
57 | margin: 0;
58 | }
59 |
60 | .img {
61 | display: block;
62 | grid-column: 1 / span 2;
63 | max-width: 100%;
64 | }
65 |
66 | .img-wrap figcaption {
67 | max-width: 200px;
68 | align-self: end;
69 | }
70 |
71 | .img-wrap figcaption span {
72 | display: block;
73 | }
74 |
75 | .img-wrap-1 {
76 | grid-column: 2 / span 3;
77 | grid-row: 1;
78 | }
79 |
80 | .img-wrap-2 {
81 | grid-column: 4 / span 3;
82 | grid-row: 2;
83 | }
84 |
85 | .img-wrap-3 {
86 | grid-column: 3 / span 3;
87 | grid-row: 3;
88 | }
89 |
90 | .img-wrap-4 {
91 | grid-column: 1 / span 3;
92 | grid-row: 4;
93 | }
94 |
95 | .img-wrap-5 {
96 | grid-column: 4 / span 3;
97 | grid-row: 5;
98 | }
99 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankohlbach/codrops-shader-on-scroll/8120ad43def74a1243761a3f5f064cc1262afc2a/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | On-Scroll Image Distortion and Grain Effect | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | X05Kenji Sato
35 |
36 |
37 |
38 | M33Yusuke Tanaka
39 |
40 |
41 |
42 | Y78Mei Yamamoto
43 |
44 |
45 |
46 | K08Natsumi Ito
47 |
48 |
49 |
50 | F03Miku Inoue
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/js/script.js:
--------------------------------------------------------------------------------
1 | import Lenis from 'lenis'
2 | import gsap from 'gsap'
3 | import { CustomEase } from 'gsap/all'
4 | import * as THREE from 'three'
5 |
6 | import { resizeThreeCanvas, calcFov, debounce, lerp } from './utils'
7 |
8 | import baseVertex from '../shader/baseVertex.glsl'
9 | import baseFragment from '../shader/baseFragment.glsl'
10 | import effectVertex from '../shader/effectVertex.glsl'
11 | import effectFragment from '../shader/effectFragment.glsl'
12 |
13 | gsap.registerPlugin(CustomEase)
14 |
15 | // smooth scroll (lenis)
16 | let scroll = {
17 | scrollY: window.scrollY,
18 | scrollVelocity: 0
19 | }
20 |
21 | const lenis = new Lenis()
22 |
23 | lenis.on('scroll', (e) => {
24 | scroll.scrollY = window.scrollY
25 | scroll.scrollVelocity = e.velocity
26 | })
27 |
28 | function scrollRaf(time) {
29 | lenis.raf(time)
30 | requestAnimationFrame(scrollRaf)
31 | }
32 |
33 | requestAnimationFrame(scrollRaf)
34 |
35 | // cursor position
36 | let cursorPos = {
37 | current: { x: 0.5, y: 0.5 },
38 | target: { x: 0.5, y: 0.5 }
39 | }
40 |
41 | let cursorRaf
42 |
43 | const lerpCursorPos = () => {
44 | const x = lerp(cursorPos.current.x, cursorPos.target.x, 0.05)
45 | const y = lerp(cursorPos.current.y, cursorPos.target.y, 0.05)
46 |
47 | cursorPos.current.x = x
48 | cursorPos.current.y = y
49 |
50 | const delta = Math.sqrt(
51 | ((cursorPos.target.x - cursorPos.current.x) ** 2) +
52 | ((cursorPos.target.y - cursorPos.current.y) ** 2)
53 | )
54 |
55 | if (delta < 0.001 && cursorRaf) {
56 | cancelAnimationFrame(cursorRaf)
57 | cursorRaf = null
58 | return
59 | }
60 |
61 | cursorRaf = requestAnimationFrame(lerpCursorPos)
62 | }
63 |
64 | window.addEventListener('mousemove', (event) => {
65 | cursorPos.target.x = (event.clientX / window.innerWidth)
66 | cursorPos.target.y = (event.clientY / window.innerHeight)
67 |
68 | if (!cursorRaf) {
69 | cursorRaf = requestAnimationFrame(lerpCursorPos)
70 | }
71 | })
72 |
73 | // helper for image-to-webgl and uniform updates
74 | // this lerps when entering the texture with cursor
75 | const handleMouseEnter = (index) => {
76 | gsap.to(
77 | mediaStore[index],
78 | { mouseEnter: 1, duration: 0.6, ease: CustomEase.create('custom', '0.4, 0, 0.2, 1') }
79 | )
80 | }
81 |
82 | // this updates the cursor position uniform on the texture
83 | const handleMousePos = (e, index) => {
84 | const bounds = mediaStore[index].media.getBoundingClientRect()
85 | const x = e.offsetX / bounds.width
86 | const y = e.offsetY / bounds.height
87 |
88 | mediaStore[index].mouseOverPos.target.x = x
89 | mediaStore[index].mouseOverPos.target.y = y
90 | }
91 |
92 | // this lerps when leaving the texture with cursor
93 | const handleMouseLeave = (index) => {
94 | gsap.to(
95 | mediaStore[index],
96 | { mouseEnter: 0, duration: 0.6, ease: CustomEase.create('custom', '0.4, 0, 0.2, 1') }
97 | )
98 | gsap.to(
99 | mediaStore[index].mouseOverPos.target,
100 | { x: 0.5, y: 0.5, duration: 0.6, ease: CustomEase.create('custom', '0.4, 0, 0.2, 1') }
101 | )
102 | }
103 |
104 | // this gets all image html tags and creates a mesh for each
105 | const setMediaStore = (scrollY) => {
106 | const media = [...document.querySelectorAll('[data-webgl-media]')]
107 |
108 | mediaStore = media.map((media, i) => {
109 | observer.observe(media)
110 |
111 | media.dataset.index = String(i)
112 | media.addEventListener('mouseenter', () => handleMouseEnter(i))
113 | media.addEventListener('mousemove', e => handleMousePos(e, i))
114 | media.addEventListener('mouseleave', () => handleMouseLeave(i))
115 |
116 | const bounds = media.getBoundingClientRect()
117 | const imageMaterial = material.clone()
118 |
119 | const imageMesh = new THREE.Mesh(geometry, imageMaterial)
120 |
121 | let texture = null
122 |
123 | texture = new THREE.Texture(media)
124 | texture.needsUpdate = true
125 |
126 | imageMaterial.uniforms.uTexture.value = texture
127 | imageMaterial.uniforms.uTextureSize.value.x = media.naturalWidth
128 | imageMaterial.uniforms.uTextureSize.value.y = media.naturalHeight
129 | imageMaterial.uniforms.uQuadSize.value.x = bounds.width
130 | imageMaterial.uniforms.uQuadSize.value.y = bounds.height
131 | imageMaterial.uniforms.uBorderRadius.value = getComputedStyle(media).borderRadius.replace('px', '')
132 |
133 | imageMesh.scale.set(bounds.width, bounds.height, 1)
134 |
135 | if (!(bounds.top >= 0 && bounds.top <= window.innerHeight)) {
136 | imageMesh.position.y = 2 * window.innerHeight
137 | }
138 |
139 | scene.add(imageMesh)
140 |
141 | return {
142 | media,
143 | material: imageMaterial,
144 | mesh: imageMesh,
145 | width: bounds.width,
146 | height: bounds.height,
147 | top: bounds.top + scrollY,
148 | left: bounds.left,
149 | isInView: bounds.top >= -500 && bounds.top <= window.innerHeight + 500,
150 | mouseEnter: 0,
151 | mouseOverPos: {
152 | current: {
153 | x: 0.5,
154 | y: 0.5
155 | },
156 | target: {
157 | x: 0.5,
158 | y: 0.5
159 | }
160 | }
161 | }
162 | })
163 | }
164 |
165 | // this sets the position of the mesh based on the scroll position
166 | const setPositions = () => {
167 | mediaStore.forEach((object) => {
168 | if (object.isInView) {
169 | object.mesh.position.x = object.left - window.innerWidth / 2 + object.width / 2
170 | object.mesh.position.y = -object.top + window.innerHeight / 2 - object.height / 2 + scroll.scrollY
171 | }
172 | })
173 | }
174 |
175 | // shader
176 | const CAMERA_POS = 500
177 |
178 | const canvas = document.querySelector('canvas')
179 |
180 | let observer
181 | let mediaStore
182 | let scene
183 | let geometry
184 | let material
185 |
186 | // create intersection observer to only render in view elements
187 | observer = new IntersectionObserver(
188 | (entries) => {
189 | entries.forEach((entry) => {
190 | const index = entry.target.dataset.index
191 |
192 | if (index) {
193 | mediaStore[parseInt(index)].isInView = entry.isIntersecting
194 | }
195 | })
196 | },
197 | { rootMargin: '500px 0px 500px 0px' }
198 | )
199 |
200 | // scene
201 | scene = new THREE.Scene()
202 |
203 | // camera
204 | const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 10, 1000)
205 | camera.position.z = CAMERA_POS
206 | camera.fov = calcFov(CAMERA_POS)
207 | camera.updateProjectionMatrix()
208 |
209 | // geometry and material
210 | geometry = new THREE.PlaneGeometry(1, 1, 100, 100)
211 | material = new THREE.ShaderMaterial({
212 | uniforms: {
213 | uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
214 | uTime: { value: 0 },
215 | uCursor: { value: new THREE.Vector2(0.5, 0.5) },
216 | uScrollVelocity: { value: 0 },
217 | uTexture: { value: null },
218 | uTextureSize: { value: new THREE.Vector2(100, 100) },
219 | uQuadSize: { value: new THREE.Vector2(100, 100) },
220 | uBorderRadius: { value: 0 },
221 | uMouseEnter: { value: 0 },
222 | uMouseOverPos: { value: new THREE.Vector2(0.5, 0.5) }
223 | },
224 | vertexShader: effectVertex,
225 | fragmentShader: effectFragment,
226 | glslVersion: THREE.GLSL3
227 | })
228 |
229 | // renderer
230 | const renderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true, antialias: true })
231 | renderer.setSize(window.innerWidth, window.innerHeight)
232 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
233 |
234 | // render loop
235 | const render = (time = 0) => {
236 | time /= 1000
237 |
238 | mediaStore.forEach((object) => {
239 | if (object.isInView) {
240 | object.mouseOverPos.current.x = lerp(object.mouseOverPos.current.x, object.mouseOverPos.target.x, 0.05)
241 | object.mouseOverPos.current.y = lerp(object.mouseOverPos.current.y, object.mouseOverPos.target.y, 0.05)
242 |
243 | object.material.uniforms.uResolution.value.x = window.innerWidth
244 | object.material.uniforms.uResolution.value.y = window.innerHeight
245 | object.material.uniforms.uTime.value = time
246 | object.material.uniforms.uCursor.value.x = cursorPos.current.x
247 | object.material.uniforms.uCursor.value.y = cursorPos.current.y
248 | object.material.uniforms.uScrollVelocity.value = scroll.scrollVelocity
249 | object.material.uniforms.uMouseOverPos.value.x = object.mouseOverPos.current.x
250 | object.material.uniforms.uMouseOverPos.value.y = object.mouseOverPos.current.y
251 | object.material.uniforms.uMouseEnter.value = object.mouseEnter
252 | } else {
253 | object.mesh.position.y = 2 * window.innerHeight
254 | }
255 | })
256 |
257 | setPositions()
258 |
259 | renderer.render(scene, camera)
260 |
261 | requestAnimationFrame(render)
262 | }
263 |
264 | window.addEventListener('resize', debounce(() => {
265 | const fov = calcFov(CAMERA_POS)
266 |
267 | resizeThreeCanvas({ camera, fov, renderer })
268 |
269 | mediaStore.forEach((object) => {
270 | const bounds = object.media.getBoundingClientRect()
271 | object.mesh.scale.set(bounds.width, bounds.height, 1)
272 | object.width = bounds.width
273 | object.height = bounds.height
274 | object.top = bounds.top + scroll.scrollY
275 | object.left = bounds.left
276 | object.isInView = bounds.top >= 0 && bounds.top <= window.innerHeight,
277 | object.material.uniforms.uTextureSize.value.x = object.media.naturalWidth
278 | object.material.uniforms.uTextureSize.value.y = object.media.naturalHeight
279 | object.material.uniforms.uQuadSize.value.x = bounds.width
280 | object.material.uniforms.uQuadSize.value.y = bounds.height
281 | object.material.uniforms.uBorderRadius.value = getComputedStyle(object.media).borderRadius.replace('px', '')
282 | })
283 | }))
284 |
285 | // Add the preloader logic
286 | window.addEventListener('load', () => {
287 | // media details
288 | setMediaStore(scroll.scrollY)
289 |
290 | requestAnimationFrame(render)
291 |
292 | document.body.classList.remove('loading')
293 | })
294 |
--------------------------------------------------------------------------------
/js/utils.js:
--------------------------------------------------------------------------------
1 | import { PerspectiveCamera } from 'three'
2 |
3 | export const resizeThreeCanvas = ({
4 | camera,
5 | fov = null,
6 | renderer,
7 | effectComposer = null
8 | }) => {
9 | if (camera instanceof PerspectiveCamera) {
10 | camera.aspect = window.innerWidth / window.innerHeight
11 |
12 | if (fov) {
13 | camera.fov = fov
14 | }
15 | }
16 |
17 | camera.updateProjectionMatrix()
18 |
19 | renderer.setSize(window.innerWidth, window.innerHeight)
20 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
21 |
22 | if (effectComposer) {
23 | effectComposer.setSize(window.innerWidth, window.innerHeight)
24 | }
25 | }
26 |
27 | export const calcFov = (CAMERA_POS) => 2 * Math.atan((window.innerHeight / 2) / CAMERA_POS) * 180 / Math.PI
28 |
29 | export const debounce = (func, timeout = 300) => {
30 | let timer
31 |
32 | return (...args) => {
33 | clearTimeout(timer)
34 | timer = setTimeout(() => { func.apply(this, args) }, timeout)
35 | }
36 | }
37 |
38 | export const lerp = (start, end, damping) => start * (1 - damping) + end * damping
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codrops-shader-on-scroll",
3 | "version": "1.0.0",
4 | "author": "Jan Kohlbach",
5 | "license": "MIT",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build"
10 | },
11 | "dependencies": {
12 | "gsap": "^3.12.5",
13 | "lenis": "^1.1.5",
14 | "three": "^0.166.0",
15 | "vite": "^5.3.2",
16 | "vite-plugin-glsl": "^1.3.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/shader/baseFragment.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform vec2 uResolution; // in pixel
4 | uniform float uTime; // in s
5 | uniform vec2 uCursor; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
6 | uniform float uScrollVelocity; // - (scroll up) / + (scroll down)
7 | uniform sampler2D uTexture; // texture
8 | uniform vec2 uTextureSize; // size of texture
9 | uniform vec2 uQuadSize; // size of texture element
10 | uniform float uBorderRadius; // pixel value
11 | uniform float uMouseEnter; // 0 - 1 (enter) / 1 - 0 (leave)
12 | uniform vec2 uMouseOverPos; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
13 |
14 | in vec2 vUv; // 0 (left) 0 (bottom) - 1 (right) 1 (top)
15 | in vec2 vUvCover;
16 |
17 | out vec4 outColor;
18 |
19 |
20 | void main() {
21 | // texture
22 | vec3 texture = vec3(texture(uTexture, vUvCover));
23 |
24 | // output
25 | outColor = vec4(texture, 1.0);
26 | }
27 |
--------------------------------------------------------------------------------
/shader/baseVertex.glsl:
--------------------------------------------------------------------------------
1 | uniform vec2 uResolution; // in pixel
2 | uniform float uTime; // in s
3 | uniform vec2 uCursor; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
4 | uniform float uScrollVelocity; // - (scroll up) / + (scroll down)
5 | uniform sampler2D uTexture; // texture
6 | uniform vec2 uTextureSize; // size of texture
7 | uniform vec2 uQuadSize; // size of texture element
8 | uniform float uBorderRadius; // pixel value
9 | uniform float uMouseEnter; // 0 - 1 (enter) / 1 - 0 (leave)
10 | uniform vec2 uMouseOverPos; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
11 |
12 | #include './resources/utils.glsl';
13 |
14 | out vec2 vUv; // 0 (left) 0 (bottom) - 1 (top) 1 (right)
15 | out vec2 vUvCover;
16 |
17 |
18 | void main() {
19 | vUv = uv;
20 | vUvCover = getCoverUvVert(uv, uTextureSize, uQuadSize);
21 |
22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
23 | }
24 |
--------------------------------------------------------------------------------
/shader/effectFragment.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | uniform vec2 uResolution; // in pixel
4 | uniform float uTime; // in s
5 | uniform vec2 uCursor; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
6 | uniform float uScrollVelocity; // - (scroll up) / + (scroll down)
7 | uniform sampler2D uTexture; // texture
8 | uniform vec2 uTextureSize; // size of texture
9 | uniform vec2 uQuadSize; // size of texture element
10 | uniform float uBorderRadius; // pixel value
11 | uniform float uMouseEnter; // 0 - 1 (enter) / 1 - 0 (leave)
12 | uniform vec2 uMouseOverPos; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
13 |
14 | in vec2 vUv; // 0 (left) 0 (bottom) - 1 (right) 1 (top)
15 | in vec2 vUvCover;
16 |
17 | #include './resources/noise.glsl';
18 |
19 | out vec4 outColor;
20 |
21 |
22 | void main() {
23 | vec2 texCoords = vUvCover;
24 |
25 | // aspect ratio needed to create a real circle when quadSize is not 1:1 ratio
26 | float aspectRatio = uQuadSize.y / uQuadSize.x;
27 |
28 | // create a circle following the mouse with size 15
29 | float circle = 1.0 - distance(
30 | vec2(uMouseOverPos.x, (1.0 - uMouseOverPos.y) * aspectRatio),
31 | vec2(vUv.x, vUv.y * aspectRatio)
32 | ) * 15.0;
33 |
34 | // create noise
35 | float noise = snoise(gl_FragCoord.xy);
36 |
37 | // modify texture coordinates
38 | texCoords.x += mix(0.0, circle * noise * 0.01, uMouseEnter + uScrollVelocity * 0.1);
39 | texCoords.y += mix(0.0, circle * noise * 0.01, uMouseEnter + uScrollVelocity * 0.1);
40 |
41 | // texture
42 | vec3 texture = vec3(texture(uTexture, texCoords));
43 |
44 | // output
45 | outColor = vec4(texture, 1.0);
46 | }
47 |
--------------------------------------------------------------------------------
/shader/effectVertex.glsl:
--------------------------------------------------------------------------------
1 | float PI = 3.141592653589793;
2 |
3 | uniform vec2 uResolution; // in pixel
4 | uniform float uTime; // in s
5 | uniform vec2 uCursor; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
6 | uniform float uScrollVelocity; // - (scroll up) / + (scroll down)
7 | uniform sampler2D uTexture; // texture
8 | uniform vec2 uTextureSize; // size of texture
9 | uniform vec2 uQuadSize; // size of texture element
10 | uniform float uBorderRadius; // pixel value
11 | uniform float uMouseEnter; // 0 - 1 (enter) / 1 - 0 (leave)
12 | uniform vec2 uMouseOverPos; // 0 (left) 0 (top) / 1 (right) 1 (bottom)
13 |
14 | #include './resources/utils.glsl';
15 |
16 | out vec2 vUv; // 0 (left) 0 (bottom) - 1 (top) 1 (right)
17 | out vec2 vUvCover;
18 |
19 |
20 | vec3 deformationCurve(vec3 position, vec2 uv) {
21 | position.y = position.y - (sin(uv.x * PI) * min(abs(uScrollVelocity), 5.0) * sign(uScrollVelocity) * -0.01);
22 |
23 | return position;
24 | }
25 |
26 | void main() {
27 | vUv = uv;
28 | vUvCover = getCoverUvVert(uv, uTextureSize, uQuadSize);
29 |
30 | vec3 deformedPosition = deformationCurve(position, vUvCover);
31 |
32 | gl_Position = projectionMatrix * modelViewMatrix * vec4(deformedPosition, 1.0);
33 | }
34 |
--------------------------------------------------------------------------------
/shader/resources/noise.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // Description : Array and textureless GLSL 2D simplex noise function.
3 | // Author : Ian McEwan, Ashima Arts.
4 | // Maintainer : stegu
5 | // Lastmod : 20110822 (ijm)
6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
7 | // Distributed under the MIT License. See LICENSE file.
8 | // https://github.com/ashima/webgl-noise
9 | // https://github.com/stegu/webgl-noise
10 | //
11 |
12 | vec3 mod289(vec3 x) {
13 | return x - floor(x * (1.0 / 289.0)) * 289.0;
14 | }
15 |
16 | vec2 mod289(vec2 x) {
17 | return x - floor(x * (1.0 / 289.0)) * 289.0;
18 | }
19 |
20 | vec3 permute(vec3 x) {
21 | return mod289(((x*34.0)+10.0)*x);
22 | }
23 |
24 | float snoise(vec2 v)
25 | {
26 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
27 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
28 | -0.577350269189626, // -1.0 + 2.0 * C.x
29 | 0.024390243902439); // 1.0 / 41.0
30 | // First corner
31 | vec2 i = floor(v + dot(v, C.yy) );
32 | vec2 x0 = v - i + dot(i, C.xx);
33 |
34 | // Other corners
35 | vec2 i1;
36 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
37 | //i1.y = 1.0 - i1.x;
38 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
39 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
40 | // x1 = x0 - i1 + 1.0 * C.xx ;
41 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
42 | vec4 x12 = x0.xyxy + C.xxzz;
43 | x12.xy -= i1;
44 |
45 | // Permutations
46 | i = mod289(i); // Avoid truncation effects in permutation
47 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
48 | + i.x + vec3(0.0, i1.x, 1.0 ));
49 |
50 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
51 | m = m*m ;
52 | m = m*m ;
53 |
54 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
55 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
56 |
57 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
58 | vec3 h = abs(x) - 0.5;
59 | vec3 ox = floor(x + 0.5);
60 | vec3 a0 = x - ox;
61 |
62 | // Normalise gradients implicitly by scaling m
63 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
64 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
65 |
66 | // Compute final noise value at P
67 | vec3 g;
68 | g.x = a0.x * x0.x + h.x * x0.y;
69 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
70 | return 130.0 * dot(m, g);
71 | }
72 |
--------------------------------------------------------------------------------
/shader/resources/utils.glsl:
--------------------------------------------------------------------------------
1 | // random
2 | float random(vec2 st) {
3 | return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
4 | }
5 |
6 | // contain
7 | vec2 getContainUvFrag(vec2 uv, vec2 textureSize, vec2 quadSize) {
8 | vec2 tempUv = uv - vec2(0.5);
9 |
10 | float quadAspect = quadSize.x / quadSize.y;
11 | float textureAspect = textureSize.x / textureSize.y;
12 |
13 | if (quadAspect > textureAspect) {
14 | tempUv *= vec2(quadAspect / textureAspect, 1.0);
15 | } else {
16 | tempUv *= vec2(1.0, textureAspect / quadAspect);
17 | }
18 |
19 | tempUv += vec2(0.5);
20 |
21 | return tempUv;
22 | }
23 |
24 | // cover
25 | vec2 getCoverUvVert(vec2 uv, vec2 textureSize, vec2 quadSize) {
26 | vec2 ratio = vec2(
27 | min((quadSize.x / quadSize.y) / (textureSize.x / textureSize.y), 1.0),
28 | min((quadSize.y / quadSize.x) / (textureSize.y / textureSize.x), 1.0)
29 | );
30 |
31 | return vec2(
32 | uv.x * ratio.x + (1.0 - ratio.x) * 0.5,
33 | uv.y * ratio.y + (1.0 - ratio.y) * 0.5
34 | );
35 | }
36 |
37 | vec2 getCoverUvFrag(vec2 uv, vec2 textureSize, vec2 quadSize) {
38 | vec2 tempUv = uv - vec2(0.5);
39 |
40 | float quadAspect = quadSize.x / quadSize.y;
41 | float textureAspect = textureSize.x / textureSize.y;
42 |
43 | if (quadAspect < textureAspect) {
44 | tempUv *= vec2(quadAspect / textureAspect, 1.0);
45 | } else {
46 | tempUv *= vec2(1.0, textureAspect / quadAspect);
47 | }
48 |
49 | tempUv += vec2(0.5);
50 |
51 | return tempUv;
52 | }
53 |
54 | // uv, rotation (in radians), mid (point to rotate around)
55 | vec2 rotate(vec2 uv, float rotation, vec2 mid) {
56 | return vec2(
57 | cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x,
58 | cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import glsl from 'vite-plugin-glsl'
3 |
4 | export default defineConfig({
5 | base: './', // This ensures all assets are referenced relatively
6 | plugins: [
7 | glsl(),
8 | ]
9 | })
10 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@darkroom.engineering/tempus@^0.0.46":
6 | version "0.0.46"
7 | resolved "https://registry.npmjs.org/@darkroom.engineering/tempus/-/tempus-0.0.46.tgz"
8 | integrity sha512-s5vav3KMHYezvUCl4ee5epg0oimF6M8C9gAaKxFnFaTvX2q3ywFDryIv6XLd0mRFUt3S1uHDJqKaiEcs2ZVSvw==
9 |
10 | "@esbuild/aix-ppc64@0.21.5":
11 | version "0.21.5"
12 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
13 | integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
14 |
15 | "@esbuild/android-arm64@0.21.5":
16 | version "0.21.5"
17 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
18 | integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
19 |
20 | "@esbuild/android-arm@0.21.5":
21 | version "0.21.5"
22 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
23 | integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
24 |
25 | "@esbuild/android-x64@0.21.5":
26 | version "0.21.5"
27 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
28 | integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
29 |
30 | "@esbuild/darwin-arm64@0.21.5":
31 | version "0.21.5"
32 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
33 | integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
34 |
35 | "@esbuild/darwin-x64@0.21.5":
36 | version "0.21.5"
37 | resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz"
38 | integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
39 |
40 | "@esbuild/freebsd-arm64@0.21.5":
41 | version "0.21.5"
42 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
43 | integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
44 |
45 | "@esbuild/freebsd-x64@0.21.5":
46 | version "0.21.5"
47 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
48 | integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
49 |
50 | "@esbuild/linux-arm64@0.21.5":
51 | version "0.21.5"
52 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
53 | integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
54 |
55 | "@esbuild/linux-arm@0.21.5":
56 | version "0.21.5"
57 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
58 | integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
59 |
60 | "@esbuild/linux-ia32@0.21.5":
61 | version "0.21.5"
62 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
63 | integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
64 |
65 | "@esbuild/linux-loong64@0.21.5":
66 | version "0.21.5"
67 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
68 | integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
69 |
70 | "@esbuild/linux-mips64el@0.21.5":
71 | version "0.21.5"
72 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
73 | integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
74 |
75 | "@esbuild/linux-ppc64@0.21.5":
76 | version "0.21.5"
77 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
78 | integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
79 |
80 | "@esbuild/linux-riscv64@0.21.5":
81 | version "0.21.5"
82 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
83 | integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
84 |
85 | "@esbuild/linux-s390x@0.21.5":
86 | version "0.21.5"
87 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
88 | integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
89 |
90 | "@esbuild/linux-x64@0.21.5":
91 | version "0.21.5"
92 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
93 | integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
94 |
95 | "@esbuild/netbsd-x64@0.21.5":
96 | version "0.21.5"
97 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
98 | integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
99 |
100 | "@esbuild/openbsd-x64@0.21.5":
101 | version "0.21.5"
102 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
103 | integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
104 |
105 | "@esbuild/sunos-x64@0.21.5":
106 | version "0.21.5"
107 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
108 | integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
109 |
110 | "@esbuild/win32-arm64@0.21.5":
111 | version "0.21.5"
112 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
113 | integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
114 |
115 | "@esbuild/win32-ia32@0.21.5":
116 | version "0.21.5"
117 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
118 | integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
119 |
120 | "@esbuild/win32-x64@0.21.5":
121 | version "0.21.5"
122 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
123 | integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
124 |
125 | "@rollup/pluginutils@^5.1.0":
126 | version "5.1.0"
127 | resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz"
128 | integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==
129 | dependencies:
130 | "@types/estree" "^1.0.0"
131 | estree-walker "^2.0.2"
132 | picomatch "^2.3.1"
133 |
134 | "@rollup/rollup-android-arm-eabi@4.18.0":
135 | version "4.18.0"
136 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27"
137 | integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==
138 |
139 | "@rollup/rollup-android-arm64@4.18.0":
140 | version "4.18.0"
141 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203"
142 | integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==
143 |
144 | "@rollup/rollup-darwin-arm64@4.18.0":
145 | version "4.18.0"
146 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096"
147 | integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==
148 |
149 | "@rollup/rollup-darwin-x64@4.18.0":
150 | version "4.18.0"
151 | resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz"
152 | integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==
153 |
154 | "@rollup/rollup-linux-arm-gnueabihf@4.18.0":
155 | version "4.18.0"
156 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8"
157 | integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==
158 |
159 | "@rollup/rollup-linux-arm-musleabihf@4.18.0":
160 | version "4.18.0"
161 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549"
162 | integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==
163 |
164 | "@rollup/rollup-linux-arm64-gnu@4.18.0":
165 | version "4.18.0"
166 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577"
167 | integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==
168 |
169 | "@rollup/rollup-linux-arm64-musl@4.18.0":
170 | version "4.18.0"
171 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c"
172 | integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==
173 |
174 | "@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
175 | version "4.18.0"
176 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf"
177 | integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==
178 |
179 | "@rollup/rollup-linux-riscv64-gnu@4.18.0":
180 | version "4.18.0"
181 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9"
182 | integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==
183 |
184 | "@rollup/rollup-linux-s390x-gnu@4.18.0":
185 | version "4.18.0"
186 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec"
187 | integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==
188 |
189 | "@rollup/rollup-linux-x64-gnu@4.18.0":
190 | version "4.18.0"
191 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942"
192 | integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==
193 |
194 | "@rollup/rollup-linux-x64-musl@4.18.0":
195 | version "4.18.0"
196 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d"
197 | integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==
198 |
199 | "@rollup/rollup-win32-arm64-msvc@4.18.0":
200 | version "4.18.0"
201 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf"
202 | integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==
203 |
204 | "@rollup/rollup-win32-ia32-msvc@4.18.0":
205 | version "4.18.0"
206 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54"
207 | integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==
208 |
209 | "@rollup/rollup-win32-x64-msvc@4.18.0":
210 | version "4.18.0"
211 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4"
212 | integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==
213 |
214 | "@types/estree@1.0.5", "@types/estree@^1.0.0":
215 | version "1.0.5"
216 | resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
217 | integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
218 |
219 | esbuild@^0.21.3:
220 | version "0.21.5"
221 | resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
222 | integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
223 | optionalDependencies:
224 | "@esbuild/aix-ppc64" "0.21.5"
225 | "@esbuild/android-arm" "0.21.5"
226 | "@esbuild/android-arm64" "0.21.5"
227 | "@esbuild/android-x64" "0.21.5"
228 | "@esbuild/darwin-arm64" "0.21.5"
229 | "@esbuild/darwin-x64" "0.21.5"
230 | "@esbuild/freebsd-arm64" "0.21.5"
231 | "@esbuild/freebsd-x64" "0.21.5"
232 | "@esbuild/linux-arm" "0.21.5"
233 | "@esbuild/linux-arm64" "0.21.5"
234 | "@esbuild/linux-ia32" "0.21.5"
235 | "@esbuild/linux-loong64" "0.21.5"
236 | "@esbuild/linux-mips64el" "0.21.5"
237 | "@esbuild/linux-ppc64" "0.21.5"
238 | "@esbuild/linux-riscv64" "0.21.5"
239 | "@esbuild/linux-s390x" "0.21.5"
240 | "@esbuild/linux-x64" "0.21.5"
241 | "@esbuild/netbsd-x64" "0.21.5"
242 | "@esbuild/openbsd-x64" "0.21.5"
243 | "@esbuild/sunos-x64" "0.21.5"
244 | "@esbuild/win32-arm64" "0.21.5"
245 | "@esbuild/win32-ia32" "0.21.5"
246 | "@esbuild/win32-x64" "0.21.5"
247 |
248 | estree-walker@^2.0.2:
249 | version "2.0.2"
250 | resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
251 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
252 |
253 | fsevents@~2.3.2, fsevents@~2.3.3:
254 | version "2.3.3"
255 | resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
256 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
257 |
258 | gsap@^3.12.5:
259 | version "3.12.5"
260 | resolved "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz"
261 | integrity sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ==
262 |
263 | lenis@^1.1.5:
264 | version "1.1.5"
265 | resolved "https://registry.npmjs.org/lenis/-/lenis-1.1.5.tgz"
266 | integrity sha512-RNpGFoOVFGboCdJ7EUcMTUNZC7d/AJV1ad8eBTYBdL2TuwdWJ11Mb5jx4dLreHadH8Mg2o+3uVHQHTd4txasaw==
267 | dependencies:
268 | "@darkroom.engineering/tempus" "^0.0.46"
269 |
270 | nanoid@^3.3.7:
271 | version "3.3.7"
272 | resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz"
273 | integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
274 |
275 | picocolors@^1.0.0:
276 | version "1.0.1"
277 | resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz"
278 | integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
279 |
280 | picomatch@^2.3.1:
281 | version "2.3.1"
282 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
283 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
284 |
285 | postcss@^8.4.38:
286 | version "8.4.38"
287 | resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz"
288 | integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
289 | dependencies:
290 | nanoid "^3.3.7"
291 | picocolors "^1.0.0"
292 | source-map-js "^1.2.0"
293 |
294 | rollup@^4.13.0:
295 | version "4.18.0"
296 | resolved "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz"
297 | integrity sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==
298 | dependencies:
299 | "@types/estree" "1.0.5"
300 | optionalDependencies:
301 | "@rollup/rollup-android-arm-eabi" "4.18.0"
302 | "@rollup/rollup-android-arm64" "4.18.0"
303 | "@rollup/rollup-darwin-arm64" "4.18.0"
304 | "@rollup/rollup-darwin-x64" "4.18.0"
305 | "@rollup/rollup-linux-arm-gnueabihf" "4.18.0"
306 | "@rollup/rollup-linux-arm-musleabihf" "4.18.0"
307 | "@rollup/rollup-linux-arm64-gnu" "4.18.0"
308 | "@rollup/rollup-linux-arm64-musl" "4.18.0"
309 | "@rollup/rollup-linux-powerpc64le-gnu" "4.18.0"
310 | "@rollup/rollup-linux-riscv64-gnu" "4.18.0"
311 | "@rollup/rollup-linux-s390x-gnu" "4.18.0"
312 | "@rollup/rollup-linux-x64-gnu" "4.18.0"
313 | "@rollup/rollup-linux-x64-musl" "4.18.0"
314 | "@rollup/rollup-win32-arm64-msvc" "4.18.0"
315 | "@rollup/rollup-win32-ia32-msvc" "4.18.0"
316 | "@rollup/rollup-win32-x64-msvc" "4.18.0"
317 | fsevents "~2.3.2"
318 |
319 | source-map-js@^1.2.0:
320 | version "1.2.0"
321 | resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"
322 | integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
323 |
324 | three@^0.166.0:
325 | version "0.166.0"
326 | resolved "https://registry.npmjs.org/three/-/three-0.166.0.tgz"
327 | integrity sha512-3Gw7oyZ/vCmz3RmNx1xuyNu7Ou/igDtoh953QsJh/QkAoi6B7jpkKwk05N8Y7/9bZeIE44zdC+i2KZNF+KWQ8A==
328 |
329 | vite-plugin-glsl@^1.3.0:
330 | version "1.3.0"
331 | resolved "https://registry.npmjs.org/vite-plugin-glsl/-/vite-plugin-glsl-1.3.0.tgz"
332 | integrity sha512-SzEoLet9Bp5VSozjrhUiSc3xX1+u7rCTjXAsq4qWM3u8UjilI76A9ucX/T+CRGQCe25j50GSY+9mKSGUVPET1w==
333 | dependencies:
334 | "@rollup/pluginutils" "^5.1.0"
335 |
336 | vite@^5.3.2:
337 | version "5.3.2"
338 | resolved "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz"
339 | integrity sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==
340 | dependencies:
341 | esbuild "^0.21.3"
342 | postcss "^8.4.38"
343 | rollup "^4.13.0"
344 | optionalDependencies:
345 | fsevents "~2.3.3"
346 |
--------------------------------------------------------------------------------