├── .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 |
14 |
15 |
16 |

MONOCHROME

17 | 18 |
19 |
20 |

STREET

21 | 22 |
23 |
24 |

ABSTRACT

25 | 26 |
27 |
28 |

ROSE

29 | 30 |
31 | 32 |
33 |
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 | --------------------------------------------------------------------------------