├── .gitignore
├── Harmond-ExtraBoldExpanded.otf
├── images
├── 1.jpeg
├── 2.jpeg
├── 3.jpeg
├── 4.jpeg
└── fov.png
├── index.html
├── js
├── app.js
└── shaders
│ ├── fragmentShader.glsl
│ └── vertexShader.glsl
├── package-lock.json
├── package.json
└── style.css
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .cache
--------------------------------------------------------------------------------
/Harmond-ExtraBoldExpanded.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/Harmond-ExtraBoldExpanded.otf
--------------------------------------------------------------------------------
/images/1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/images/1.jpeg
--------------------------------------------------------------------------------
/images/2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/images/2.jpeg
--------------------------------------------------------------------------------
/images/3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/images/3.jpeg
--------------------------------------------------------------------------------
/images/4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/images/4.jpeg
--------------------------------------------------------------------------------
/images/fov.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conorbailey90/rgb-split-distortion-scroll-effect/df57bb38106edd33cfdcac696d8246207f819cf3/images/fov.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import vertexShader from'./shaders/vertexShader.glsl';
3 | import fragmentShader from'./shaders/fragmentShader.glsl';
4 |
5 | let scrollable = document.querySelector('.scrollable');
6 |
7 | let current = 0;
8 | let target = 0;
9 | let ease = 0.075;
10 |
11 | // Linear inetepolation used for smooth scrolling and image offset uniform adjustment
12 |
13 | function lerp(start, end, t){
14 | return start * (1 - t ) + end * t;
15 | }
16 |
17 | // init function triggered on page load to set the body height to enable scrolling and EffectCanvas initialised
18 | function init(){
19 | document.body.style.height = `${scrollable.getBoundingClientRect().height}px`;
20 | }
21 |
22 | // translate the scrollable div using the lerp function for the smooth scrolling effect.
23 | function smoothScroll(){
24 | target = window.scrollY;
25 | current = lerp(current, target, ease);
26 | scrollable.style.transform = `translate3d(0,${-current}px, 0)`;
27 | }
28 |
29 | class EffectCanvas{
30 | constructor(){
31 | this.container = document.querySelector('main');
32 | this.images = [...document.querySelectorAll('img')];
33 | this.meshItems = []; // Used to store all meshes we will be creating.
34 | this.setupCamera();
35 | this.createMeshItems();
36 | this.render()
37 | }
38 |
39 | // Getter function used to get screen dimensions used for the camera and mesh materials
40 | get viewport(){
41 | let width = window.innerWidth;
42 | let height = window.innerHeight;
43 | let aspectRatio = width / height;
44 | return {
45 | width,
46 | height,
47 | aspectRatio
48 | };
49 | }
50 |
51 | setupCamera(){
52 |
53 | window.addEventListener('resize', this.onWindowResize.bind(this), false);
54 |
55 | // Create new scene
56 | this.scene = new THREE.Scene();
57 |
58 | // Initialize perspective camera
59 |
60 | let perspective = 1000;
61 | const fov = (180 * (2 * Math.atan(window.innerHeight / 2 / perspective))) / Math.PI; // see fov image for a picture breakdown of this fov setting.
62 | this.camera = new THREE.PerspectiveCamera(fov, this.viewport.aspectRatio, 1, 1000)
63 | this.camera.position.set(0, 0, perspective); // set the camera position on the z axis.
64 |
65 | // renderer
66 | // this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
67 | this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });
68 | this.renderer.setSize(this.viewport.width, this.viewport.height); // uses the getter viewport function above to set size of canvas / renderer
69 | this.renderer.setPixelRatio(window.devicePixelRatio); // Import to ensure image textures do not appear blurred.
70 | this.container.appendChild(this.renderer.domElement); // append the canvas to the main element
71 | }
72 |
73 | onWindowResize(){
74 | init();
75 | this.camera.aspect = this.viewport.aspectRatio; // readjust the aspect ratio.
76 | this.camera.updateProjectionMatrix(); // Used to recalulate projectin dimensions.
77 | this.renderer.setSize(this.viewport.width, this.viewport.height);
78 | }
79 |
80 | createMeshItems(){
81 | // Loop thorugh all images and create new MeshItem instances. Push these instances to the meshItems array.
82 | this.images.forEach(image => {
83 | let meshItem = new MeshItem(image, this.scene);
84 | this.meshItems.push(meshItem);
85 | })
86 | }
87 |
88 | // Animate smoothscroll and meshes. Repeatedly called using requestanimationdrame
89 | render(){
90 | smoothScroll();
91 | for(let i = 0; i < this.meshItems.length; i++){
92 | this.meshItems[i].render();
93 | }
94 | this.renderer.render(this.scene, this.camera)
95 | requestAnimationFrame(this.render.bind(this));
96 | }
97 | }
98 |
99 | class MeshItem{
100 | // Pass in the scene as we will be adding meshes to this scene.
101 | constructor(element, scene){
102 | this.element = element;
103 | this.scene = scene;
104 | this.offset = new THREE.Vector2(0,0); // Positions of mesh on screen. Will be updated below.
105 | this.sizes = new THREE.Vector2(0,0); //Size of mesh on screen. Will be updated below.
106 | this.createMesh();
107 | }
108 |
109 | getDimensions(){
110 | const {width, height, top, left} = this.element.getBoundingClientRect();
111 | this.sizes.set(width, height);
112 | this.offset.set(left - window.innerWidth / 2 + width / 2, -top + window.innerHeight / 2 - height / 2);
113 | }
114 |
115 | createMesh(){
116 | this.geometry = new THREE.PlaneBufferGeometry(1,1,100,100);
117 | this.imageTexture = new THREE.TextureLoader().load(this.element.src);
118 | this.uniforms = {
119 | uTexture: {
120 | //texture data
121 | value: this.imageTexture
122 | },
123 | uOffset: {
124 | //distortion strength
125 | value: new THREE.Vector2(0.0, 0.0)
126 | },
127 | uAlpha: {
128 | //opacity
129 | value: 1.
130 | }
131 | };
132 | this.material = new THREE.ShaderMaterial({
133 | uniforms: this.uniforms,
134 | vertexShader: vertexShader,
135 | fragmentShader: fragmentShader,
136 | transparent: true,
137 | // wireframe: true,
138 | side: THREE.DoubleSide
139 | })
140 | this.mesh = new THREE.Mesh( this.geometry, this.material );
141 | this.getDimensions(); // set offsetand sizes for placement on the scene
142 | this.mesh.position.set(this.offset.x, this.offset.y, 0);
143 | this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);
144 |
145 | this.scene.add( this.mesh );
146 | }
147 |
148 | render(){
149 | // this function is repeatidly called for each instance in the aboce
150 | this.getDimensions();
151 | this.mesh.position.set(this.offset.x, this.offset.y, 0)
152 | this.mesh.scale.set(this.sizes.x, this.sizes.y, 1)
153 | this.uniforms.uOffset.value.set(this.offset.x * 0.0, -(target- current) * 0.0003 )
154 | }
155 | }
156 |
157 | init()
158 | new EffectCanvas()
159 |
--------------------------------------------------------------------------------
/js/shaders/fragmentShader.glsl:
--------------------------------------------------------------------------------
1 | // uniform sampler2D uTexture;
2 | // uniform float uAlpha;
3 | // uniform vec2 uOffset;
4 | // varying vec2 vUv;
5 |
6 | // void main(){
7 | // gl_FragColor = vec4(255.,255.,255.,255.);
8 | // }
9 |
10 |
11 | uniform sampler2D uTexture;
12 | uniform float uAlpha;
13 | uniform vec2 uOffset;
14 | varying vec2 vUv;
15 |
16 | vec3 rgbShift(sampler2D textureImage, vec2 uv, vec2 offset) {
17 | float r = texture2D(textureImage,uv + offset).r;
18 | vec2 gb = texture2D(textureImage,uv).gb;
19 | return vec3(r,gb);
20 | }
21 |
22 | void main() {
23 | vec3 color = rgbShift(uTexture,vUv,uOffset);
24 | gl_FragColor = vec4(color,uAlpha);
25 | }
--------------------------------------------------------------------------------
/js/shaders/vertexShader.glsl:
--------------------------------------------------------------------------------
1 | // uniform sampler2D uTexture;
2 | // uniform vec2 uOffset;
3 | // varying vec2 vUv;
4 |
5 | // float M_PI = 3.141529;
6 |
7 | // void main(){
8 | // vUv = uv;
9 | // vec3 newPosition = position;
10 |
11 | // gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
12 | // }
13 |
14 |
15 | uniform sampler2D uTexture;
16 | uniform vec2 uOffset;
17 | varying vec2 vUv;
18 |
19 | #define M_PI 3.1415926535897932384626433832795
20 |
21 | vec3 deformationCurve(vec3 position, vec2 uv, vec2 offset) {
22 | position.x = position.x + (sin(uv.y * M_PI) * offset.x);
23 | position.y = position.y + (sin(uv.x * M_PI) * offset.y);
24 | return position;
25 | }
26 |
27 | void main() {
28 | vUv = uv;
29 | vec3 newPosition = deformationCurve(position, uv, uOffset);
30 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
31 | }
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rgb-split",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "version": "1.0.0",
9 | "license": "ISC",
10 | "dependencies": {
11 | "three": "^0.127.0"
12 | },
13 | "devDependencies": {
14 | "glslify-bundle": "^5.1.1",
15 | "glslify-deps": "^1.3.2"
16 | }
17 | },
18 | "node_modules/@choojs/findup": {
19 | "version": "0.2.1",
20 | "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
21 | "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
22 | "dev": true,
23 | "dependencies": {
24 | "commander": "^2.15.1"
25 | },
26 | "bin": {
27 | "findup": "bin/findup.js"
28 | }
29 | },
30 | "node_modules/commander": {
31 | "version": "2.20.3",
32 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
33 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
34 | "dev": true
35 | },
36 | "node_modules/core-util-is": {
37 | "version": "1.0.2",
38 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
39 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
40 | "dev": true
41 | },
42 | "node_modules/events": {
43 | "version": "3.3.0",
44 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
45 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
46 | "dev": true,
47 | "engines": {
48 | "node": ">=0.8.x"
49 | }
50 | },
51 | "node_modules/function-bind": {
52 | "version": "1.1.1",
53 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
54 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
55 | "dev": true
56 | },
57 | "node_modules/glsl-inject-defines": {
58 | "version": "1.0.3",
59 | "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
60 | "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=",
61 | "dev": true,
62 | "dependencies": {
63 | "glsl-token-inject-block": "^1.0.0",
64 | "glsl-token-string": "^1.0.1",
65 | "glsl-tokenizer": "^2.0.2"
66 | }
67 | },
68 | "node_modules/glsl-resolve": {
69 | "version": "0.0.1",
70 | "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz",
71 | "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=",
72 | "dev": true,
73 | "dependencies": {
74 | "resolve": "^0.6.1",
75 | "xtend": "^2.1.2"
76 | }
77 | },
78 | "node_modules/glsl-resolve/node_modules/resolve": {
79 | "version": "0.6.3",
80 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
81 | "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=",
82 | "dev": true
83 | },
84 | "node_modules/glsl-token-assignments": {
85 | "version": "2.0.2",
86 | "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz",
87 | "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8=",
88 | "dev": true
89 | },
90 | "node_modules/glsl-token-defines": {
91 | "version": "1.0.0",
92 | "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz",
93 | "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=",
94 | "dev": true,
95 | "dependencies": {
96 | "glsl-tokenizer": "^2.0.0"
97 | }
98 | },
99 | "node_modules/glsl-token-depth": {
100 | "version": "1.1.2",
101 | "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz",
102 | "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ=",
103 | "dev": true
104 | },
105 | "node_modules/glsl-token-descope": {
106 | "version": "1.0.2",
107 | "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz",
108 | "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=",
109 | "dev": true,
110 | "dependencies": {
111 | "glsl-token-assignments": "^2.0.0",
112 | "glsl-token-depth": "^1.1.0",
113 | "glsl-token-properties": "^1.0.0",
114 | "glsl-token-scope": "^1.1.0"
115 | }
116 | },
117 | "node_modules/glsl-token-inject-block": {
118 | "version": "1.1.0",
119 | "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz",
120 | "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ=",
121 | "dev": true
122 | },
123 | "node_modules/glsl-token-properties": {
124 | "version": "1.0.1",
125 | "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz",
126 | "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4=",
127 | "dev": true
128 | },
129 | "node_modules/glsl-token-scope": {
130 | "version": "1.1.2",
131 | "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz",
132 | "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E=",
133 | "dev": true
134 | },
135 | "node_modules/glsl-token-string": {
136 | "version": "1.0.1",
137 | "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz",
138 | "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw=",
139 | "dev": true
140 | },
141 | "node_modules/glsl-token-whitespace-trim": {
142 | "version": "1.0.0",
143 | "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz",
144 | "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA=",
145 | "dev": true
146 | },
147 | "node_modules/glsl-tokenizer": {
148 | "version": "2.1.5",
149 | "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
150 | "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
151 | "dev": true,
152 | "dependencies": {
153 | "through2": "^0.6.3"
154 | }
155 | },
156 | "node_modules/glslify-bundle": {
157 | "version": "5.1.1",
158 | "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz",
159 | "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==",
160 | "dev": true,
161 | "dependencies": {
162 | "glsl-inject-defines": "^1.0.1",
163 | "glsl-token-defines": "^1.0.0",
164 | "glsl-token-depth": "^1.1.1",
165 | "glsl-token-descope": "^1.0.2",
166 | "glsl-token-scope": "^1.1.1",
167 | "glsl-token-string": "^1.0.1",
168 | "glsl-token-whitespace-trim": "^1.0.0",
169 | "glsl-tokenizer": "^2.0.2",
170 | "murmurhash-js": "^1.0.0",
171 | "shallow-copy": "0.0.1"
172 | }
173 | },
174 | "node_modules/glslify-deps": {
175 | "version": "1.3.2",
176 | "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz",
177 | "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==",
178 | "dev": true,
179 | "dependencies": {
180 | "@choojs/findup": "^0.2.0",
181 | "events": "^3.2.0",
182 | "glsl-resolve": "0.0.1",
183 | "glsl-tokenizer": "^2.0.0",
184 | "graceful-fs": "^4.1.2",
185 | "inherits": "^2.0.1",
186 | "map-limit": "0.0.1",
187 | "resolve": "^1.0.0"
188 | }
189 | },
190 | "node_modules/graceful-fs": {
191 | "version": "4.2.6",
192 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
193 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
194 | "dev": true
195 | },
196 | "node_modules/has": {
197 | "version": "1.0.3",
198 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
199 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
200 | "dev": true,
201 | "dependencies": {
202 | "function-bind": "^1.1.1"
203 | },
204 | "engines": {
205 | "node": ">= 0.4.0"
206 | }
207 | },
208 | "node_modules/inherits": {
209 | "version": "2.0.4",
210 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
211 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
212 | "dev": true
213 | },
214 | "node_modules/is-core-module": {
215 | "version": "2.2.0",
216 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
217 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
218 | "dev": true,
219 | "dependencies": {
220 | "has": "^1.0.3"
221 | },
222 | "funding": {
223 | "url": "https://github.com/sponsors/ljharb"
224 | }
225 | },
226 | "node_modules/isarray": {
227 | "version": "0.0.1",
228 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
229 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
230 | "dev": true
231 | },
232 | "node_modules/map-limit": {
233 | "version": "0.0.1",
234 | "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
235 | "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
236 | "dev": true,
237 | "dependencies": {
238 | "once": "~1.3.0"
239 | }
240 | },
241 | "node_modules/murmurhash-js": {
242 | "version": "1.0.0",
243 | "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
244 | "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=",
245 | "dev": true
246 | },
247 | "node_modules/once": {
248 | "version": "1.3.3",
249 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
250 | "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
251 | "dev": true,
252 | "dependencies": {
253 | "wrappy": "1"
254 | }
255 | },
256 | "node_modules/path-parse": {
257 | "version": "1.0.6",
258 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
259 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
260 | "dev": true
261 | },
262 | "node_modules/readable-stream": {
263 | "version": "1.0.34",
264 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
265 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
266 | "dev": true,
267 | "dependencies": {
268 | "core-util-is": "~1.0.0",
269 | "inherits": "~2.0.1",
270 | "isarray": "0.0.1",
271 | "string_decoder": "~0.10.x"
272 | }
273 | },
274 | "node_modules/resolve": {
275 | "version": "1.20.0",
276 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
277 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
278 | "dev": true,
279 | "dependencies": {
280 | "is-core-module": "^2.2.0",
281 | "path-parse": "^1.0.6"
282 | },
283 | "funding": {
284 | "url": "https://github.com/sponsors/ljharb"
285 | }
286 | },
287 | "node_modules/shallow-copy": {
288 | "version": "0.0.1",
289 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
290 | "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=",
291 | "dev": true
292 | },
293 | "node_modules/string_decoder": {
294 | "version": "0.10.31",
295 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
296 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
297 | "dev": true
298 | },
299 | "node_modules/three": {
300 | "version": "0.127.0",
301 | "resolved": "https://registry.npmjs.org/three/-/three-0.127.0.tgz",
302 | "integrity": "sha512-wtgrn+mhYUbobxT7QN3GPdu3SRpSBQvwY6uOzLChWS7QE//f7paDU/+wlzbg+ngeIvBBqjBHSRuywTh8A99Jng=="
303 | },
304 | "node_modules/through2": {
305 | "version": "0.6.5",
306 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
307 | "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
308 | "dev": true,
309 | "dependencies": {
310 | "readable-stream": ">=1.0.33-1 <1.1.0-0",
311 | "xtend": ">=4.0.0 <4.1.0-0"
312 | }
313 | },
314 | "node_modules/through2/node_modules/xtend": {
315 | "version": "4.0.2",
316 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
317 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
318 | "dev": true,
319 | "engines": {
320 | "node": ">=0.4"
321 | }
322 | },
323 | "node_modules/wrappy": {
324 | "version": "1.0.2",
325 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
326 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
327 | "dev": true
328 | },
329 | "node_modules/xtend": {
330 | "version": "2.2.0",
331 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz",
332 | "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak=",
333 | "dev": true,
334 | "engines": {
335 | "node": ">=0.4"
336 | }
337 | }
338 | },
339 | "dependencies": {
340 | "@choojs/findup": {
341 | "version": "0.2.1",
342 | "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
343 | "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
344 | "dev": true,
345 | "requires": {
346 | "commander": "^2.15.1"
347 | }
348 | },
349 | "commander": {
350 | "version": "2.20.3",
351 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
352 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
353 | "dev": true
354 | },
355 | "core-util-is": {
356 | "version": "1.0.2",
357 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
358 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
359 | "dev": true
360 | },
361 | "events": {
362 | "version": "3.3.0",
363 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
364 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
365 | "dev": true
366 | },
367 | "function-bind": {
368 | "version": "1.1.1",
369 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
370 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
371 | "dev": true
372 | },
373 | "glsl-inject-defines": {
374 | "version": "1.0.3",
375 | "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
376 | "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=",
377 | "dev": true,
378 | "requires": {
379 | "glsl-token-inject-block": "^1.0.0",
380 | "glsl-token-string": "^1.0.1",
381 | "glsl-tokenizer": "^2.0.2"
382 | }
383 | },
384 | "glsl-resolve": {
385 | "version": "0.0.1",
386 | "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz",
387 | "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=",
388 | "dev": true,
389 | "requires": {
390 | "resolve": "^0.6.1",
391 | "xtend": "^2.1.2"
392 | },
393 | "dependencies": {
394 | "resolve": {
395 | "version": "0.6.3",
396 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
397 | "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=",
398 | "dev": true
399 | }
400 | }
401 | },
402 | "glsl-token-assignments": {
403 | "version": "2.0.2",
404 | "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz",
405 | "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8=",
406 | "dev": true
407 | },
408 | "glsl-token-defines": {
409 | "version": "1.0.0",
410 | "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz",
411 | "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=",
412 | "dev": true,
413 | "requires": {
414 | "glsl-tokenizer": "^2.0.0"
415 | }
416 | },
417 | "glsl-token-depth": {
418 | "version": "1.1.2",
419 | "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz",
420 | "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ=",
421 | "dev": true
422 | },
423 | "glsl-token-descope": {
424 | "version": "1.0.2",
425 | "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz",
426 | "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=",
427 | "dev": true,
428 | "requires": {
429 | "glsl-token-assignments": "^2.0.0",
430 | "glsl-token-depth": "^1.1.0",
431 | "glsl-token-properties": "^1.0.0",
432 | "glsl-token-scope": "^1.1.0"
433 | }
434 | },
435 | "glsl-token-inject-block": {
436 | "version": "1.1.0",
437 | "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz",
438 | "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ=",
439 | "dev": true
440 | },
441 | "glsl-token-properties": {
442 | "version": "1.0.1",
443 | "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz",
444 | "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4=",
445 | "dev": true
446 | },
447 | "glsl-token-scope": {
448 | "version": "1.1.2",
449 | "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz",
450 | "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E=",
451 | "dev": true
452 | },
453 | "glsl-token-string": {
454 | "version": "1.0.1",
455 | "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz",
456 | "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw=",
457 | "dev": true
458 | },
459 | "glsl-token-whitespace-trim": {
460 | "version": "1.0.0",
461 | "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz",
462 | "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA=",
463 | "dev": true
464 | },
465 | "glsl-tokenizer": {
466 | "version": "2.1.5",
467 | "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
468 | "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
469 | "dev": true,
470 | "requires": {
471 | "through2": "^0.6.3"
472 | }
473 | },
474 | "glslify-bundle": {
475 | "version": "5.1.1",
476 | "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz",
477 | "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==",
478 | "dev": true,
479 | "requires": {
480 | "glsl-inject-defines": "^1.0.1",
481 | "glsl-token-defines": "^1.0.0",
482 | "glsl-token-depth": "^1.1.1",
483 | "glsl-token-descope": "^1.0.2",
484 | "glsl-token-scope": "^1.1.1",
485 | "glsl-token-string": "^1.0.1",
486 | "glsl-token-whitespace-trim": "^1.0.0",
487 | "glsl-tokenizer": "^2.0.2",
488 | "murmurhash-js": "^1.0.0",
489 | "shallow-copy": "0.0.1"
490 | }
491 | },
492 | "glslify-deps": {
493 | "version": "1.3.2",
494 | "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz",
495 | "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==",
496 | "dev": true,
497 | "requires": {
498 | "@choojs/findup": "^0.2.0",
499 | "events": "^3.2.0",
500 | "glsl-resolve": "0.0.1",
501 | "glsl-tokenizer": "^2.0.0",
502 | "graceful-fs": "^4.1.2",
503 | "inherits": "^2.0.1",
504 | "map-limit": "0.0.1",
505 | "resolve": "^1.0.0"
506 | }
507 | },
508 | "graceful-fs": {
509 | "version": "4.2.6",
510 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
511 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
512 | "dev": true
513 | },
514 | "has": {
515 | "version": "1.0.3",
516 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
517 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
518 | "dev": true,
519 | "requires": {
520 | "function-bind": "^1.1.1"
521 | }
522 | },
523 | "inherits": {
524 | "version": "2.0.4",
525 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
526 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
527 | "dev": true
528 | },
529 | "is-core-module": {
530 | "version": "2.2.0",
531 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
532 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
533 | "dev": true,
534 | "requires": {
535 | "has": "^1.0.3"
536 | }
537 | },
538 | "isarray": {
539 | "version": "0.0.1",
540 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
541 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
542 | "dev": true
543 | },
544 | "map-limit": {
545 | "version": "0.0.1",
546 | "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
547 | "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
548 | "dev": true,
549 | "requires": {
550 | "once": "~1.3.0"
551 | }
552 | },
553 | "murmurhash-js": {
554 | "version": "1.0.0",
555 | "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
556 | "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=",
557 | "dev": true
558 | },
559 | "once": {
560 | "version": "1.3.3",
561 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
562 | "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
563 | "dev": true,
564 | "requires": {
565 | "wrappy": "1"
566 | }
567 | },
568 | "path-parse": {
569 | "version": "1.0.6",
570 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
571 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
572 | "dev": true
573 | },
574 | "readable-stream": {
575 | "version": "1.0.34",
576 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
577 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
578 | "dev": true,
579 | "requires": {
580 | "core-util-is": "~1.0.0",
581 | "inherits": "~2.0.1",
582 | "isarray": "0.0.1",
583 | "string_decoder": "~0.10.x"
584 | }
585 | },
586 | "resolve": {
587 | "version": "1.20.0",
588 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
589 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
590 | "dev": true,
591 | "requires": {
592 | "is-core-module": "^2.2.0",
593 | "path-parse": "^1.0.6"
594 | }
595 | },
596 | "shallow-copy": {
597 | "version": "0.0.1",
598 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
599 | "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=",
600 | "dev": true
601 | },
602 | "string_decoder": {
603 | "version": "0.10.31",
604 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
605 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
606 | "dev": true
607 | },
608 | "three": {
609 | "version": "0.127.0",
610 | "resolved": "https://registry.npmjs.org/three/-/three-0.127.0.tgz",
611 | "integrity": "sha512-wtgrn+mhYUbobxT7QN3GPdu3SRpSBQvwY6uOzLChWS7QE//f7paDU/+wlzbg+ngeIvBBqjBHSRuywTh8A99Jng=="
612 | },
613 | "through2": {
614 | "version": "0.6.5",
615 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
616 | "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
617 | "dev": true,
618 | "requires": {
619 | "readable-stream": ">=1.0.33-1 <1.1.0-0",
620 | "xtend": ">=4.0.0 <4.1.0-0"
621 | },
622 | "dependencies": {
623 | "xtend": {
624 | "version": "4.0.2",
625 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
626 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
627 | "dev": true
628 | }
629 | }
630 | },
631 | "wrappy": {
632 | "version": "1.0.2",
633 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
634 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
635 | "dev": true
636 | },
637 | "xtend": {
638 | "version": "2.2.0",
639 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz",
640 | "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak=",
641 | "dev": true
642 | }
643 | }
644 | }
645 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rgb-split",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "three": "^0.127.0"
14 | },
15 | "devDependencies": {
16 | "glslify-bundle": "^5.1.1",
17 | "glslify-deps": "^1.3.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Harmond";
3 | src: url("/Harmond-ExtraBoldExpanded.otf") format("opentype");
4 | }
5 |
6 | * {
7 | margin: 0;
8 | padding: 0;
9 | box-sizing: border-box;
10 | font-family: Harmond;
11 | }
12 |
13 | body,
14 | html {
15 | overscroll-behavior: none;
16 | background-color: #000000;
17 | height: 100vh;
18 | width: 100%;
19 | }
20 |
21 | main {
22 | position: fixed;
23 | width: 100%;
24 | height: 100vh;
25 | }
26 |
27 | .scrollable {
28 | position: absolute;
29 | top: 0;
30 | left: 0;
31 | width: 100%;
32 | will-change: transform;
33 | }
34 |
35 | .image-container {
36 | position: relative;
37 | width: 100%;
38 | height: 100vh;
39 | overflow: hidden;
40 | display: flex;
41 | align-items: center;
42 | justify-content: center;
43 | }
44 | h1 {
45 | position: absolute;
46 | left: 0;
47 | top: 60%;
48 | left: 55%;
49 | z-index: 10;
50 | color: #ffffff;
51 | mix-blend-mode: difference;
52 | }
53 |
54 | img {
55 | position: absolute;
56 | height: 60%;
57 | visibility: hidden;
58 | }
59 |
60 | canvas {
61 | position: fixed;
62 | z-index: -10;
63 | top: 0;
64 | left: 0;
65 | }
66 |
--------------------------------------------------------------------------------