├── .gitignore ├── LICENSE ├── README.md ├── css └── base.css ├── favicon.ico ├── img ├── 1.jpg ├── 2.jpg ├── 3.jpg └── 4.jpg ├── index.html ├── index2.html ├── index3.html ├── index4.html ├── js ├── app.js └── shader │ ├── fragment.glsl │ ├── vertex.glsl │ └── vertexParticles.glsl └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | .parcel-cache 4 | package-lock.json 5 | dist/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2009 - 2021 [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 | # Distorted Pixels 2 | 3 | Distorting pixels with DataTexture, based on [Infinite Bad Guy website](https://billie.withyoutube.com/) 4 | 5 | ![Image Title](https://tympanus.net/codrops/wp-content/uploads/2022/01/DistrotedPixels_feat.jpg) 6 | 7 | [Article on Codrops](https://tympanus.net/codrops/?p=) 8 | 9 | [Demo](http://tympanus.net/Development/DistortedPixels/) 10 | 11 | 12 | ## Installation 13 | 14 | Install dependencies: 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | Compile the code for development and start a local server: 21 | 22 | ``` 23 | npx parcel index.html 24 | ``` 25 | 26 | Create the build: 27 | 28 | ``` 29 | npx parcel build index.html 30 | ``` 31 | 32 | ## Credits 33 | 34 | - Images from [Unsplash](https://unsplash.com/) 35 | 36 | ## Misc 37 | 38 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/) 39 | 40 | ## License 41 | [MIT](LICENSE) 42 | 43 | Made with :blue_heart: by [Codrops](http://www.codrops.com) 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /css/base.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | font-size: 18px; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | --color-text: #fff; 14 | --color-bg: #000; 15 | --color-link: #c63643; 16 | --color-link-hover: #fff; 17 | color: var(--color-text); 18 | background-color: var(--color-bg); 19 | font-family: mono45-headline, monospace; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | 24 | .demo-2 { 25 | --color-link: #b95e5d; 26 | } 27 | 28 | .demo-3 { 29 | --color-link: #f7bcac; 30 | } 31 | 32 | .demo-4 { 33 | --color-link: #bf8b5b; 34 | } 35 | 36 | .lil-gui { 37 | visibility: hidden; 38 | } 39 | 40 | .demo-4 .lil-gui { 41 | visibility: visible; 42 | } 43 | 44 | /* Page Loader */ 45 | .js .loading::before, 46 | .js .loading::after { 47 | content: ''; 48 | position: fixed; 49 | z-index: 1000; 50 | } 51 | 52 | .js .loading::before { 53 | top: 0; 54 | left: 0; 55 | width: 100%; 56 | height: 100%; 57 | background: var(--color-bg); 58 | } 59 | 60 | .js .loading::after { 61 | top: 50%; 62 | left: 50%; 63 | width: 60px; 64 | height: 60px; 65 | margin: -30px 0 0 -30px; 66 | border-radius: 50%; 67 | opacity: 0.4; 68 | background: var(--color-link); 69 | animation: loaderAnim 0.7s linear infinite alternate forwards; 70 | 71 | } 72 | 73 | @keyframes loaderAnim { 74 | to { 75 | opacity: 1; 76 | transform: scale3d(0.5,0.5,1); 77 | } 78 | } 79 | 80 | a { 81 | text-decoration: none; 82 | color: var(--color-link); 83 | outline: none; 84 | } 85 | 86 | a:hover { 87 | color: var(--color-link-hover); 88 | outline: none; 89 | } 90 | 91 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */ 92 | a:focus { 93 | /* Provide a fallback style for browsers 94 | that don't support :focus-visible */ 95 | outline: none; 96 | background: lightgrey; 97 | } 98 | 99 | a:focus:not(:focus-visible) { 100 | /* Remove the focus indicator on mouse-focus for browsers 101 | that do support :focus-visible */ 102 | background: transparent; 103 | } 104 | 105 | a:focus-visible { 106 | /* Draw a very noticeable focus style for 107 | keyboard-focus on browsers that do support 108 | :focus-visible */ 109 | outline: 2px solid red; 110 | background: transparent; 111 | } 112 | 113 | .unbutton { 114 | background: none; 115 | border: 0; 116 | padding: 0; 117 | margin: 0; 118 | font: inherit; 119 | } 120 | 121 | .unbutton:focus { 122 | outline: none; 123 | } 124 | 125 | .frame { 126 | padding: 3rem 5vw; 127 | text-align: center; 128 | position: relative; 129 | z-index: 1000; 130 | } 131 | 132 | .frame__title { 133 | font-size: 1rem; 134 | margin: 0 0 1rem; 135 | font-weight: normal; 136 | } 137 | 138 | .frame__links { 139 | display: inline; 140 | } 141 | 142 | .frame__links a:not(:last-child), 143 | .frame__demos a:not(:last-child) { 144 | margin-right: 1rem; 145 | } 146 | 147 | .frame__demos { 148 | margin: 1rem 0; 149 | } 150 | 151 | .frame__demo--current, 152 | .frame__demo--current:hover { 153 | color: var(--color-text); 154 | } 155 | 156 | .content { 157 | display: grid; 158 | width: 100vw; 159 | height: calc(100vh - 13rem); 160 | position: relative; 161 | padding: 3rem; 162 | } 163 | 164 | .content__title { 165 | font-family: mono45-headline, monospace; 166 | font-size: 13vw; 167 | line-height: 1; 168 | color: #fff; 169 | max-width: 50vw; 170 | font-weight: 400; 171 | margin: 0; 172 | align-self: center; 173 | pointer-events: none; 174 | } 175 | 176 | .content__title--centered { 177 | text-align: center; 178 | justify-self: center; 179 | } 180 | 181 | .content__title--style-1 { 182 | font-family: new-order, sans-serif; 183 | font-weight: 400; 184 | font-size: 10vw; 185 | line-height: 0.85; 186 | background: linear-gradient(90deg, #e9a680 0%,#992d46 50%, #064cb5 100%);; 187 | background-size: cover; 188 | -webkit-background-clip: text; 189 | -webkit-text-fill-color: transparent; 190 | background-clip: text; 191 | text-fill-color: transparent; 192 | } 193 | 194 | .content__title--style-2 { 195 | font-family: bely-display, serif; 196 | font-weight: 400; 197 | font-size: 10vw; 198 | opacity: 0.7; 199 | line-height: 0.8; 200 | } 201 | 202 | 203 | @media screen and (min-width: 53em) { 204 | .frame { 205 | position: fixed; 206 | text-align: left; 207 | z-index: 100; 208 | top: 0; 209 | left: 0; 210 | display: grid; 211 | align-content: space-between; 212 | width: 100%; 213 | max-width: none; 214 | height: 100vh; 215 | padding: 1.5rem 3.35rem; 216 | pointer-events: none; 217 | grid-template-columns: auto 1fr; 218 | grid-template-rows: auto auto auto; 219 | grid-template-areas: 'title ...' 220 | '... ...' 221 | 'links demos'; 222 | } 223 | .frame__title-wrap { 224 | grid-area: title; 225 | display: flex; 226 | } 227 | .frame__title { 228 | margin: 0; 229 | } 230 | .frame__tagline { 231 | position: relative; 232 | margin: 0 0 0 0.25rem; 233 | padding: 0 0 0 1rem; 234 | opacity: 0.5; 235 | } 236 | .frame__demos { 237 | margin: 0; 238 | grid-area: demos; 239 | justify-self: end; 240 | } 241 | .frame__links { 242 | grid-area: links; 243 | padding: 0; 244 | justify-self: start; 245 | } 246 | .frame a { 247 | pointer-events: auto; 248 | } 249 | .content { 250 | height: 100vh; 251 | } 252 | } 253 | #canvasContainer{ 254 | position: fixed; 255 | top: 0; 256 | left: 0; 257 | z-index: -1; 258 | width: 100%; 259 | height: 100%; 260 | } 261 | #canvasContainer img{ 262 | visibility: hidden; 263 | pointer-events: none; 264 | position: absolute; 265 | } 266 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/DistortedPixels/807e5497d9da9177df700d5694f9e3a5fd6df03a/favicon.ico -------------------------------------------------------------------------------- /img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/DistortedPixels/807e5497d9da9177df700d5694f9e3a5fd6df03a/img/1.jpg -------------------------------------------------------------------------------- /img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/DistortedPixels/807e5497d9da9177df700d5694f9e3a5fd6df03a/img/2.jpg -------------------------------------------------------------------------------- /img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/DistortedPixels/807e5497d9da9177df700d5694f9e3a5fd6df03a/img/3.jpg -------------------------------------------------------------------------------- /img/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akella/DistortedPixels/807e5497d9da9177df700d5694f9e3a5fd6df03a/img/4.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixel Distortion with Three.js | Demo 1 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |

Pixel Distortion

21 |

with Three.js

22 |
23 | 28 | 34 |
35 |
40 | 41 |
42 |
43 |

We distort realities

44 |
45 |
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixel Distortion with Three.js | Demo 2 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |

Pixel Distortion

21 |

with Three.js

22 |
23 | 28 | 34 |
35 |
41 | 42 |
43 |
44 |

Fungible Love

45 |
46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /index3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixel Distortion with Three.js | Demo 3 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |

Pixel Distortion

21 |

with Three.js

22 |
23 | 28 | 34 |
35 |
40 | 41 |
42 |
43 |

Cold Storage

44 |
45 |
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /index4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixel Distortion with Three.js | Demo 4 | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |

Pixel Distortion

21 |

with Three.js

22 |
23 | 28 | 34 |
35 |
40 | 41 |
42 |
43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import fragment from "./shader/fragment.glsl"; 3 | import vertex from "./shader/vertex.glsl"; 4 | import GUI from "lil-gui"; 5 | 6 | function clamp(number, min, max) { 7 | return Math.max(min, Math.min(number, max)); 8 | } 9 | 10 | export default class Sketch { 11 | constructor(options) { 12 | this.scene = new THREE.Scene(); 13 | 14 | this.container = options.dom; 15 | this.img = this.container.querySelector('img') 16 | this.width = this.container.offsetWidth; 17 | this.height = this.container.offsetHeight; 18 | this.renderer = new THREE.WebGLRenderer(); 19 | this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) 20 | this.renderer.setSize(this.width, this.height); 21 | this.renderer.setClearColor(0xeeeeee, 1); 22 | this.renderer.physicallyCorrectLights = true; 23 | this.renderer.outputEncoding = THREE.sRGBEncoding; 24 | 25 | this.container.appendChild(this.renderer.domElement); 26 | 27 | this.camera = new THREE.PerspectiveCamera( 28 | 70, 29 | window.innerWidth / window.innerHeight, 30 | 0.1, 31 | 100 32 | ); 33 | 34 | var frustumSize = 1; 35 | var aspect = window.innerWidth / window.innerHeight; 36 | this.camera = new THREE.OrthographicCamera(frustumSize / -2, frustumSize / 2, frustumSize / 2, frustumSize / -2, -1000, 1000); 37 | this.camera.position.set(0, 0, 2); 38 | 39 | this.time = 0; 40 | 41 | this.mouse = { 42 | x: 0, 43 | y: 0, 44 | prevX: 0, 45 | prevY: 0, 46 | vX: 0, 47 | vY: 0 48 | } 49 | 50 | this.isPlaying = true; 51 | this.settings(); 52 | this.addObjects(); 53 | this.resize(); 54 | this.render(); 55 | this.setupResize(); 56 | 57 | this.mouseEvents() 58 | 59 | } 60 | 61 | getValue(val){ 62 | return parseFloat(this.container.getAttribute('data-'+val)) 63 | } 64 | 65 | 66 | mouseEvents() { 67 | window.addEventListener('mousemove', (e) => { 68 | this.mouse.x = e.clientX / this.width; 69 | this.mouse.y = e.clientY / this.height; 70 | 71 | // console.log(this.mouse.x,this.mouse.y) 72 | 73 | this.mouse.vX = this.mouse.x - this.mouse.prevX; 74 | this.mouse.vY = this.mouse.y - this.mouse.prevY; 75 | 76 | 77 | this.mouse.prevX = this.mouse.x 78 | this.mouse.prevY = this.mouse.y; 79 | 80 | 81 | // console.log(this.mouse.vX,'vx') 82 | }) 83 | } 84 | 85 | settings() { 86 | let that = this; 87 | this.settings = { 88 | grid: this.getValue('grid')||34, 89 | mouse: this.getValue('mouse')||0.25, 90 | strength: this.getValue('strength')||1, 91 | relaxation: this.getValue('relaxation')||0.9, 92 | }; 93 | 94 | 95 | this.gui = new GUI(); 96 | 97 | this.gui.add(this.settings, "grid", 2, 1000, 1).onFinishChange(() => { 98 | this.regenerateGrid() 99 | }); 100 | this.gui.add(this.settings, "mouse", 0, 1, 0.01); 101 | this.gui.add(this.settings, "strength", 0, 1, 0.01); 102 | this.gui.add(this.settings, "relaxation", 0, 1, 0.01); 103 | } 104 | 105 | setupResize() { 106 | window.addEventListener("resize", this.resize.bind(this)); 107 | } 108 | 109 | resize() { 110 | this.width = this.container.offsetWidth; 111 | this.height = this.container.offsetHeight; 112 | this.renderer.setSize(this.width, this.height); 113 | this.camera.aspect = this.width / this.height; 114 | 115 | 116 | // image cover 117 | this.imageAspect = 1. / 1.5; 118 | let a1; 119 | let a2; 120 | if (this.height / this.width > this.imageAspect) { 121 | a1 = (this.width / this.height) * this.imageAspect; 122 | a2 = 1; 123 | } else { 124 | a1 = 1; 125 | a2 = (this.height / this.width) / this.imageAspect; 126 | } 127 | 128 | this.material.uniforms.resolution.value.x = this.width; 129 | this.material.uniforms.resolution.value.y = this.height; 130 | this.material.uniforms.resolution.value.z = a1; 131 | this.material.uniforms.resolution.value.w = a2; 132 | 133 | this.camera.updateProjectionMatrix(); 134 | this.regenerateGrid() 135 | 136 | 137 | } 138 | 139 | regenerateGrid() { 140 | this.size = this.settings.grid; 141 | 142 | const width = this.size; 143 | const height = this.size; 144 | 145 | const size = width * height; 146 | const data = new Float32Array(3 * size); 147 | const color = new THREE.Color(0xffffff); 148 | 149 | const r = Math.floor(color.r * 255); 150 | const g = Math.floor(color.g * 255); 151 | const b = Math.floor(color.b * 255); 152 | 153 | for (let i = 0; i < size; i++) { 154 | let r = Math.random() * 255 - 125; 155 | let r1 = Math.random() * 255 - 125; 156 | 157 | const stride = i * 3; 158 | 159 | data[stride] = r; 160 | data[stride + 1] = r1; 161 | data[stride + 2] = r; 162 | 163 | } 164 | 165 | // used the buffer to create a DataTexture 166 | 167 | this.texture = new THREE.DataTexture(data, width, height, THREE.RGBFormat, THREE.FloatType); 168 | 169 | this.texture.magFilter = this.texture.minFilter = THREE.NearestFilter; 170 | 171 | if (this.material) { 172 | this.material.uniforms.uDataTexture.value = this.texture; 173 | this.material.uniforms.uDataTexture.value.needsUpdate = true; 174 | } 175 | } 176 | 177 | addObjects() { 178 | 179 | this.regenerateGrid() 180 | let texture = new THREE.Texture(this.img) 181 | texture.needsUpdate = true; 182 | this.material = new THREE.ShaderMaterial({ 183 | extensions: { 184 | derivatives: "#extension GL_OES_standard_derivatives : enable" 185 | }, 186 | side: THREE.DoubleSide, 187 | uniforms: { 188 | time: { 189 | value: 0 190 | }, 191 | resolution: { 192 | value: new THREE.Vector4() 193 | }, 194 | uTexture: { 195 | value: texture 196 | }, 197 | uDataTexture: { 198 | value: this.texture 199 | }, 200 | }, 201 | vertexShader: vertex, 202 | fragmentShader: fragment 203 | }); 204 | 205 | this.geometry = new THREE.PlaneGeometry(1, 1, 1, 1); 206 | 207 | this.plane = new THREE.Mesh(this.geometry, this.material); 208 | this.scene.add(this.plane); 209 | } 210 | 211 | 212 | updateDataTexture() { 213 | let data = this.texture.image.data; 214 | for (let i = 0; i < data.length; i += 3) { 215 | data[i] *= this.settings.relaxation 216 | data[i + 1] *= this.settings.relaxation 217 | } 218 | 219 | let gridMouseX = this.size * this.mouse.x; 220 | let gridMouseY = this.size * (1 - this.mouse.y); 221 | let maxDist = this.size * this.settings.mouse; 222 | let aspect = this.height / this.width 223 | 224 | for (let i = 0; i < this.size; i++) { 225 | for (let j = 0; j < this.size; j++) { 226 | 227 | let distance = ((gridMouseX - i) ** 2) / aspect + (gridMouseY - j) ** 2 228 | let maxDistSq = maxDist ** 2; 229 | 230 | if (distance < maxDistSq) { 231 | 232 | let index = 3 * (i + this.size * j); 233 | 234 | let power = maxDist / Math.sqrt(distance); 235 | power = clamp(power, 0, 10) 236 | // if(distance