├── docs ├── CNAME ├── bundle.js.tmp-browserify-61102682381603701245 ├── bundle.js.tmp-browserify-79130417987854184680 ├── bundle.js.tmp-browserify-83488444191712263276 ├── bundle.js.tmp-browserify-85151847811409409061 ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── site.webmanifest ├── demos │ ├── gloweyes.glsl │ ├── big-mouth.glsl │ ├── potion-seller.glsl │ ├── rainbow.glsl │ ├── tie-die.glsl │ ├── babies.glsl │ ├── six-eyes.glsl │ ├── censor.glsl │ ├── triple.glsl │ ├── donut.glsl │ ├── alien.glsl │ ├── creep.glsl │ ├── ice.glsl │ ├── barcode.glsl │ ├── godrays.glsl │ ├── cute.glsl │ ├── cabbibo.glsl │ ├── starter.glsl │ ├── dither.glsl │ └── rayblob.glsl ├── lucario.css ├── index.html ├── main.css └── codemirror.css ├── demo.png ├── .screenshot.png ├── src ├── vertex.glsl ├── pack.shader.js ├── share.js ├── fade.js ├── reference.js ├── loadingShader.glsl ├── setup-facemesh.js ├── paint.js ├── editor.js ├── prefix.glsl └── triangulation.js ├── demos ├── gloweyes.glsl ├── big-mouth.glsl ├── potion-seller.glsl ├── rainbow.glsl ├── tie-die.glsl ├── babies.glsl ├── six-eyes.glsl ├── censor.glsl ├── triple.glsl ├── donut.glsl ├── alien.glsl ├── creep.glsl ├── ice.glsl ├── barcode.glsl ├── godrays.glsl ├── cute.glsl ├── cabbibo.glsl ├── starter.glsl ├── dither.glsl └── rayblob.glsl ├── server ├── package.json ├── serve.js └── package-lock.json ├── .vscode └── settings.json ├── README.md ├── package.json ├── LICENSE ├── .gitignore └── index.js /docs/CNAME: -------------------------------------------------------------------------------- 1 | shaderbooth.com -------------------------------------------------------------------------------- /docs/bundle.js.tmp-browserify-61102682381603701245: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/bundle.js.tmp-browserify-79130417987854184680: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/bundle.js.tmp-browserify-83488444191712263276: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/bundle.js.tmp-browserify-85151847811409409061: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/demo.png -------------------------------------------------------------------------------- /.screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/.screenshot.png -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/favicon-16x16.png -------------------------------------------------------------------------------- /docs/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/favicon-32x32.png -------------------------------------------------------------------------------- /docs/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxBittker/shaderbooth/HEAD/docs/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/vertex.glsl: -------------------------------------------------------------------------------- 1 | 2 | // boring "pass-through" vertex shader 3 | precision mediump float; 4 | attribute vec2 position; 5 | varying vec2 uv; 6 | 7 | void main () { 8 | uv = position; 9 | gl_Position = vec4(position, 0, 1); 10 | } -------------------------------------------------------------------------------- /docs/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /src/pack.shader.js: -------------------------------------------------------------------------------- 1 | const glslify = require("glslify"); 2 | const path = require("path"); 3 | 4 | module.exports = require("shader-reload")({ 5 | vertex: glslify(path.resolve(__dirname, "vertex.glsl")), 6 | fragment: glslify(path.resolve(__dirname, "./../demos/starter.glsl")) 7 | }); 8 | -------------------------------------------------------------------------------- /src/share.js: -------------------------------------------------------------------------------- 1 | let openShare = document.getElementById("open-share"); 2 | console.log(openShare); 3 | openShare.addEventListener("click", () => { 4 | console.log("clck"); 5 | document.getElementById("share-box").classList.toggle("hidden"); 6 | openShare.classList.toggle("open"); 7 | }); 8 | -------------------------------------------------------------------------------- /demos/gloweyes.glsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | void main() { 4 | 5 | vec3 cam = getCam(uv); 6 | 7 | vec3 color = cam; 8 | 9 | float dif = length(uv - leftEye) * 10.; 10 | dif = min(dif, length(uv - rightEye) * 10.); 11 | dif = min(dif, 1.0); 12 | color = cam / vec3(dif); 13 | gl_FragColor = vec4(color, 1); 14 | } 15 | -------------------------------------------------------------------------------- /docs/demos/gloweyes.glsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | void main() { 4 | 5 | vec3 cam = getCam(uv); 6 | 7 | vec3 color = cam; 8 | 9 | float dif = length(uv - leftEye) * 10.; 10 | dif = min(dif, length(uv - rightEye) * 10.); 11 | dif = min(dif, 1.0); 12 | color = cam / vec3(dif); 13 | gl_FragColor = vec4(color, 1); 14 | } 15 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "serve.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/big-mouth.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | vec2 pos = uv; 6 | 7 | pos -= faceCenter; 8 | pos /= 2.0; 9 | pos += faceCenter; 10 | pos.y -= 0.1; 11 | 12 | if (getMouth(pos) > 0.3) { 13 | color = getCam(pos); 14 | } 15 | 16 | pos.y += 0.2; 17 | pos.x *= 1.5; 18 | if (getEye(pos) > 0.6) { 19 | // big eyes: 20 | // color = getCam(pos); 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /docs/demos/big-mouth.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | vec2 pos = uv; 6 | 7 | pos -= faceCenter; 8 | pos /= 2.0; 9 | pos += faceCenter; 10 | pos.y -= 0.1; 11 | 12 | if (getMouth(pos) > 0.3) { 13 | color = getCam(pos); 14 | } 15 | 16 | pos.y += 0.2; 17 | pos.x *= 1.5; 18 | if (getEye(pos) > 0.6) { 19 | // big eyes: 20 | // color = getCam(pos); 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /demos/potion-seller.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | float distanceFromCenter = length(uv - faceCenter); 4 | distanceFromCenter = clamp(distanceFromCenter, 0.0, 0.5); 5 | 6 | float howMuchToRotate = map(distanceFromCenter, 0.0, 0.5, 1.0, 0.0); 7 | howMuchToRotate = pow(howMuchToRotate, 3.) * 0.5; 8 | 9 | vec2 position = rotate(uv, howMuchToRotate); 10 | position *= 1.0 + howMuchToRotate * 2.; 11 | 12 | vec3 color = getCam(position); 13 | 14 | gl_FragColor = vec4(color, 1); 15 | } 16 | -------------------------------------------------------------------------------- /docs/demos/potion-seller.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | float distanceFromCenter = length(uv - faceCenter); 4 | distanceFromCenter = clamp(distanceFromCenter, 0.0, 0.5); 5 | 6 | float howMuchToRotate = map(distanceFromCenter, 0.0, 0.5, 1.0, 0.0); 7 | howMuchToRotate = pow(howMuchToRotate, 3.) * 0.5; 8 | 9 | vec2 position = rotate(uv, howMuchToRotate); 10 | position *= 1.0 + howMuchToRotate * 2.; 11 | 12 | vec3 color = getCam(position); 13 | 14 | gl_FragColor = vec4(color, 1); 15 | } 16 | -------------------------------------------------------------------------------- /demos/rainbow.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | if (getFace(uv) < 0.5) { 5 | pos.x += sin(400. + pos.y * 90.) * 0.01; 6 | pos.y += sin(pos.x * 90.) * 0.01; 7 | } 8 | vec3 color = getCam(pos); 9 | float l = luma(color); 10 | vec3 hsl = rgb2hsl(color); 11 | float hue = (l + 50. + mod(time, 60.)) * 0.1; 12 | hue *= l * 1.; 13 | hue = mod(hue, 1.0); 14 | color = hsl2rgb(hue, 0.7, hsl.z); 15 | if (getFace(uv) > 0.5) { 16 | color = hsl2rgb(hue * 0.7 + 0.1, 0.7, hsl.z); 17 | } 18 | gl_FragColor = vec4(color, 1); 19 | } -------------------------------------------------------------------------------- /docs/demos/rainbow.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | if (getFace(uv) < 0.5) { 5 | pos.x += sin(400. + pos.y * 90.) * 0.01; 6 | pos.y += sin(pos.x * 90.) * 0.01; 7 | } 8 | vec3 color = getCam(pos); 9 | float l = luma(color); 10 | vec3 hsl = rgb2hsl(color); 11 | float hue = (l + 50. + mod(time, 60.)) * 0.1; 12 | hue *= l * 1.; 13 | hue = mod(hue, 1.0); 14 | color = hsl2rgb(hue, 0.7, hsl.z); 15 | if (getFace(uv) > 0.5) { 16 | color = hsl2rgb(hue * 0.7 + 0.1, 0.7, hsl.z); 17 | } 18 | gl_FragColor = vec4(color, 1); 19 | } -------------------------------------------------------------------------------- /demos/tie-die.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | if (getFace(uv) < 0.5) { 5 | pos.x += sin(5. + pos.y * 90.) * 0.02; 6 | pos.y += sin(pos.x * 1290.) * 0.05; 7 | } 8 | vec3 color = getCam(pos); 9 | float l = luma(color); 10 | vec3 hsl = rgb2hsl(color); 11 | float hue = (l + mod(time, 30.) + 300.) * 0.1; 12 | hue *= l * 0.02; 13 | hue += faceCenter.x + faceCenter.y; 14 | 15 | hue = mod(hue, 1.0); 16 | color = hsl2rgb(hue, 0.7, hsl.z); 17 | color = hsl2rgb(hue * 1.2 + 0.1, 0.7, hsl.z); 18 | 19 | gl_FragColor = vec4(color, 1); 20 | } 21 | -------------------------------------------------------------------------------- /docs/demos/tie-die.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | if (getFace(uv) < 0.5) { 5 | pos.x += sin(5. + pos.y * 90.) * 0.02; 6 | pos.y += sin(pos.x * 1290.) * 0.05; 7 | } 8 | vec3 color = getCam(pos); 9 | float l = luma(color); 10 | vec3 hsl = rgb2hsl(color); 11 | float hue = (l + mod(time, 30.) + 300.) * 0.1; 12 | hue *= l * 0.02; 13 | hue += faceCenter.x + faceCenter.y; 14 | 15 | hue = mod(hue, 1.0); 16 | color = hsl2rgb(hue, 0.7, hsl.z); 17 | color = hsl2rgb(hue * 1.2 + 0.1, 0.7, hsl.z); 18 | 19 | gl_FragColor = vec4(color, 1); 20 | } 21 | -------------------------------------------------------------------------------- /demos/babies.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | // try uncommenting this line: 6 | // color = getPrevious(uv + (color.rg - color.bb) * 0.1); 7 | 8 | for (int i = 0; i < 6; i++) { 9 | float a = time + TAU * float(i) / 6.; 10 | vec2 offset = vec2(sin(a), cos(a)); 11 | offset.x /= targetAspect; 12 | offset *= length(leftEye - rightEye) * 3.; 13 | offset += faceCenter; 14 | vec2 pos = uv * 2.4; 15 | if (getFace(pos - offset) > 0.1) { 16 | color = getCam(pos - offset); 17 | } 18 | } 19 | 20 | gl_FragColor = vec4(color, 1); 21 | } 22 | -------------------------------------------------------------------------------- /docs/demos/babies.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | // try uncommenting this line: 6 | // color = getPrevious(uv + (color.rg - color.bb) * 0.1); 7 | 8 | for (int i = 0; i < 6; i++) { 9 | float a = time + TAU * float(i) / 6.; 10 | vec2 offset = vec2(sin(a), cos(a)); 11 | offset.x /= targetAspect; 12 | offset *= length(leftEye - rightEye) * 3.; 13 | offset += faceCenter; 14 | vec2 pos = uv * 2.4; 15 | if (getFace(pos - offset) > 0.1) { 16 | color = getCam(pos - offset); 17 | } 18 | } 19 | 20 | gl_FragColor = vec4(color, 1); 21 | } 22 | -------------------------------------------------------------------------------- /src/fade.js: -------------------------------------------------------------------------------- 1 | let fadeTimout = 1000 * 10; 2 | let timeout = window.setTimeout(() => { 3 | document.body.classList.add("faded"); 4 | }, fadeTimout); 5 | 6 | let handleActivity = e => { 7 | window.clearTimeout(timeout); 8 | document.body.classList.remove("faded"); 9 | timeout = window.setTimeout(() => { 10 | document.body.classList.add("faded"); 11 | }, fadeTimout); 12 | }; 13 | document.body.addEventListener("mousemove", handleActivity); 14 | document.body.addEventListener("keydown", handleActivity); 15 | document.body.addEventListener("touchstart", handleActivity); 16 | 17 | module.exports = handleActivity; 18 | -------------------------------------------------------------------------------- /demos/six-eyes.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | vec3 color = cam; 5 | 6 | vec2 eveVector = leftEye - rightEye; 7 | float occularDistance = length(eveVector); 8 | 9 | vec2 offset = vec2(occularDistance * 0.5); 10 | 11 | eveVector = normalize(eveVector); 12 | eveVector = vec2(-eveVector.y, eveVector.x); 13 | 14 | offset *= eveVector; 15 | 16 | if (getEye(uv - offset) > 0.6) { 17 | color = getPrevious(uv - offset * 2.); 18 | } 19 | 20 | offset *= -1.0; 21 | 22 | if (getEye(uv - offset) > 0.6) { 23 | color = getPrevious(uv - offset); 24 | } 25 | 26 | gl_FragColor = vec4(color, 1); 27 | } 28 | -------------------------------------------------------------------------------- /docs/demos/six-eyes.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | vec3 color = cam; 5 | 6 | vec2 eveVector = leftEye - rightEye; 7 | float occularDistance = length(eveVector); 8 | 9 | vec2 offset = vec2(occularDistance * 0.5); 10 | 11 | eveVector = normalize(eveVector); 12 | eveVector = vec2(-eveVector.y, eveVector.x); 13 | 14 | offset *= eveVector; 15 | 16 | if (getEye(uv - offset) > 0.6) { 17 | color = getPrevious(uv - offset * 2.); 18 | } 19 | 20 | offset *= -1.0; 21 | 22 | if (getEye(uv - offset) > 0.6) { 23 | color = getPrevious(uv - offset); 24 | } 25 | 26 | gl_FragColor = vec4(color, 1); 27 | } 28 | -------------------------------------------------------------------------------- /demos/censor.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | float face = getFace(uv); 5 | vec3 color = cam; 6 | 7 | vec2 offset = pixel * 10.; 8 | vec3 blur = getCam(uv + vec2(offset.x, 0.)) / 4.; 9 | blur += getCam(uv + vec2(-offset.x, 0.)) / 4.; 10 | blur += getCam(uv + vec2(0., offset.y)) / 4.; 11 | blur += getCam(uv + vec2(0., -offset.y)) / 4.; 12 | 13 | float nChunks = 30.; 14 | vec2 chunkedPos = (floor(uv * nChunks) + 0.5) / nChunks; 15 | vec3 pixelate = getCam(chunkedPos); 16 | if (face > 0.2) { 17 | 18 | color = pixelate; 19 | // uncomment: 20 | // color = blur; 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /docs/demos/censor.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | float face = getFace(uv); 5 | vec3 color = cam; 6 | 7 | vec2 offset = pixel * 10.; 8 | vec3 blur = getCam(uv + vec2(offset.x, 0.)) / 4.; 9 | blur += getCam(uv + vec2(-offset.x, 0.)) / 4.; 10 | blur += getCam(uv + vec2(0., offset.y)) / 4.; 11 | blur += getCam(uv + vec2(0., -offset.y)) / 4.; 12 | 13 | float nChunks = 30.; 14 | vec2 chunkedPos = (floor(uv * nChunks) + 0.5) / nChunks; 15 | vec3 pixelate = getCam(chunkedPos); 16 | if (face > 0.2) { 17 | 18 | color = pixelate; 19 | // uncomment: 20 | // color = blur; 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /demos/triple.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | vec3 prev = getPrevious(uv * 1.05 + pixel * 20.); 5 | float face = getFace(uv); 6 | 7 | vec3 color = cam; 8 | 9 | // try uncommenting this line: 10 | // color = getPrevious(uv + (cam.rg - cam.bb) * 0.1); 11 | if (getFace(uv) > 0.2) { 12 | color = cam; 13 | } 14 | float offset = 0.4; 15 | // offset *= sin(time*2.); 16 | if (getFace(uv - vec2(offset, 0.)) > face) { 17 | color = getCam(uv - vec2(offset, 0.)); 18 | } 19 | 20 | offset *= -1.; 21 | if (getFace(uv - vec2(offset, 0.)) > face) { 22 | color = getCam(uv - vec2(offset, 0.)); 23 | } 24 | 25 | gl_FragColor = vec4(color, 1); 26 | } 27 | -------------------------------------------------------------------------------- /docs/demos/triple.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | vec3 prev = getPrevious(uv * 1.05 + pixel * 20.); 5 | float face = getFace(uv); 6 | 7 | vec3 color = cam; 8 | 9 | // try uncommenting this line: 10 | // color = getPrevious(uv + (cam.rg - cam.bb) * 0.1); 11 | if (getFace(uv) > 0.2) { 12 | color = cam; 13 | } 14 | float offset = 0.4; 15 | // offset *= sin(time*2.); 16 | if (getFace(uv - vec2(offset, 0.)) > face) { 17 | color = getCam(uv - vec2(offset, 0.)); 18 | } 19 | 20 | offset *= -1.; 21 | if (getFace(uv - vec2(offset, 0.)) > face) { 22 | color = getCam(uv - vec2(offset, 0.)); 23 | } 24 | 25 | gl_FragColor = vec4(color, 1); 26 | } 27 | -------------------------------------------------------------------------------- /demos/donut.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | // try uncommenting this line: 6 | // color = getPrevious(uv + (color.rg - color.bb) * 0.1); 7 | 8 | float distToCenter = length(uv - faceCenter); 9 | int faceHit = 0; 10 | for (int i = 0; i < 20; i++) { 11 | float a = time + TAU * float(i) / 20.; 12 | vec2 offset = vec2(sin(a), cos(a)); 13 | offset *= 2.5 * distToCenter; 14 | vec2 pos = uv * 4.; 15 | if (getFace(pos - offset) > 0.1) { 16 | faceHit = 1; 17 | color = getCam(pos - offset); 18 | } 19 | } 20 | 21 | if (faceHit == 0) { 22 | color = getPrevious(uv * 1.03) * .9; 23 | } 24 | // color = vec3(0.0); 25 | gl_FragColor = vec4(color, 1); 26 | } 27 | -------------------------------------------------------------------------------- /docs/demos/donut.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | // try uncommenting this line: 6 | // color = getPrevious(uv + (color.rg - color.bb) * 0.1); 7 | 8 | float distToCenter = length(uv - faceCenter); 9 | int faceHit = 0; 10 | for (int i = 0; i < 20; i++) { 11 | float a = time + TAU * float(i) / 20.; 12 | vec2 offset = vec2(sin(a), cos(a)); 13 | offset *= 2.5 * distToCenter; 14 | vec2 pos = uv * 4.; 15 | if (getFace(pos - offset) > 0.1) { 16 | faceHit = 1; 17 | color = getCam(pos - offset); 18 | } 19 | } 20 | 21 | if (faceHit == 0) { 22 | color = getPrevious(uv * 1.03) * .9; 23 | } 24 | // color = vec3(0.0); 25 | gl_FragColor = vec4(color, 1); 26 | } 27 | -------------------------------------------------------------------------------- /demos/alien.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | vec3 color = getCam(uv); 5 | // color = getPrevious(pos+pixel*30.); 6 | 7 | for (int i = 0; i < 10; i++) { 8 | float scale = 1.0 - float(i) * 0.02; // * abs(sin(time)); 9 | vec2 offset = vec2(0.2, 0.01) * float(i) * 0.0001; 10 | float wiggleAngle = noise(vec3(uv, float(i) + time * 0.2) * 0.334) * TAU; 11 | 12 | offset += vec2(sin(wiggleAngle), cos(wiggleAngle)) * 0.1; 13 | vec2 epos = (pos * scale) + offset; 14 | 15 | if (getMouth(epos) > 0.5 && getMouth(epos) < 1.5) { 16 | color = getCam(epos); 17 | } 18 | if (getEye(epos) > 0.6) { 19 | color = getCam(epos); 20 | } 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /docs/demos/alien.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec2 pos = uv; 4 | vec3 color = getCam(uv); 5 | // color = getPrevious(pos+pixel*30.); 6 | 7 | for (int i = 0; i < 10; i++) { 8 | float scale = 1.0 - float(i) * 0.02; // * abs(sin(time)); 9 | vec2 offset = vec2(0.2, 0.01) * float(i) * 0.0001; 10 | float wiggleAngle = noise(vec3(uv, float(i) + time * 0.2) * 0.334) * TAU; 11 | 12 | offset += vec2(sin(wiggleAngle), cos(wiggleAngle)) * 0.1; 13 | vec2 epos = (pos * scale) + offset; 14 | 15 | if (getMouth(epos) > 0.5 && getMouth(epos) < 1.5) { 16 | color = getCam(epos); 17 | } 18 | if (getEye(epos) > 0.6) { 19 | color = getCam(epos); 20 | } 21 | } 22 | 23 | gl_FragColor = vec4(color, 1); 24 | } 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.enabledLanguageIds": [ 3 | "asciidoc", 4 | "c", 5 | "cpp", 6 | "csharp", 7 | "css", 8 | "git-commit", 9 | "go", 10 | "handlebars", 11 | "haskell", 12 | "html", 13 | "jade", 14 | "java", 15 | "javascriptreact", 16 | "jsonc", 17 | "latex", 18 | "less", 19 | "markdown", 20 | "php", 21 | "plaintext", 22 | "pug", 23 | "python", 24 | "restructuredtext", 25 | "rust", 26 | "scala", 27 | "scss", 28 | "text", 29 | "typescript", 30 | "typescriptreact", 31 | "yaml", 32 | "yml" 33 | ] 34 | } -------------------------------------------------------------------------------- /demos/creep.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 wcolor = getCam(uv).rgb; 4 | float wmag = luma(wcolor); 5 | wcolor = hsl2rgb((sin(time * 0.001) * 0.5) + 1.0, 0.2, wmag + 0.5); 6 | 7 | int n = 5; 8 | float uB = luma(getPrevious(uv + pixel * vec2(0., 2.0)).rgb); 9 | float dB = luma(getPrevious(uv + pixel * vec2(0, -2.0)).rgb); 10 | float lB = luma(getPrevious(uv + pixel * vec2(-2.0, 0.)).rgb); 11 | float rB = luma(getPrevious(uv + pixel * vec2(2.0, 0.)).rgb); 12 | 13 | vec2 d = vec2(rB - lB, dB - uB); 14 | 15 | vec3 scolor = getPrevious(uv + d * pixel * 10.).rgb; 16 | 17 | vec3 color = wcolor; 18 | 19 | if (luma(wcolor) > luma(scolor) /*webcam darker*/ 20 | && luma(wcolor) * 0.7 + sin(time * 1.) * 0.1 < luma(scolor)) { 21 | color = scolor; 22 | } 23 | if (getFace(uv) < 0.4) { 24 | color += vec3(0.001); 25 | } 26 | gl_FragColor.rgb = color; 27 | 28 | gl_FragColor.a = 1.0; 29 | } -------------------------------------------------------------------------------- /docs/demos/creep.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 wcolor = getCam(uv).rgb; 4 | float wmag = luma(wcolor); 5 | wcolor = hsl2rgb((sin(time * 0.001) * 0.5) + 1.0, 0.2, wmag + 0.5); 6 | 7 | int n = 5; 8 | float uB = luma(getPrevious(uv + pixel * vec2(0., 2.0)).rgb); 9 | float dB = luma(getPrevious(uv + pixel * vec2(0, -2.0)).rgb); 10 | float lB = luma(getPrevious(uv + pixel * vec2(-2.0, 0.)).rgb); 11 | float rB = luma(getPrevious(uv + pixel * vec2(2.0, 0.)).rgb); 12 | 13 | vec2 d = vec2(rB - lB, dB - uB); 14 | 15 | vec3 scolor = getPrevious(uv + d * pixel * 10.).rgb; 16 | 17 | vec3 color = wcolor; 18 | 19 | if (luma(wcolor) > luma(scolor) /*webcam darker*/ 20 | && luma(wcolor) * 0.7 + sin(time * 1.) * 0.1 < luma(scolor)) { 21 | color = scolor; 22 | } 23 | if (getFace(uv) < 0.4) { 24 | color += vec3(0.001); 25 | } 26 | gl_FragColor.rgb = color; 27 | 28 | gl_FragColor.a = 1.0; 29 | } -------------------------------------------------------------------------------- /src/reference.js: -------------------------------------------------------------------------------- 1 | let infoButton = document.getElementById("info"); 2 | infoButton.addEventListener("click", () => { 3 | document.getElementById("info-box").classList.toggle("hidden"); 4 | infoButton.classList.toggle("open"); 5 | }); 6 | 7 | let fs = require("fs"); 8 | 9 | let prefix = fs.readFileSync(__dirname + "/prefix.glsl").toString(); 10 | 11 | var CodeMirror = require("codemirror/lib/codemirror"); 12 | require("codemirror/mode/clike/clike"); 13 | require("codemirror/addon/runmode/runmode"); 14 | require("codemirror/addon/runmode/colorize"); 15 | 16 | let reference = document.getElementById("reference"); 17 | prefix = prefix.replace("precision highp float;\n", ""); 18 | 19 | prefix = prefix.replace(/ *\{[^]*?^\}/gm, "{…}"); 20 | prefix = prefix.replace(/ *\{[^]*?\} */g, "{…}"); 21 | 22 | prefix = prefix.replace(/\/\/ INTERNAL\n.*\n/g, ""); 23 | prefix = prefix.replace(/\n\n/g, "\n"); 24 | prefix = prefix.replace(/\n\n/g, "\n"); 25 | prefix = prefix.replace(/\/\/ SPACER\n/g, "\n"); 26 | 27 | CodeMirror.runMode(prefix, "x-shader/x-fragment", reference); 28 | -------------------------------------------------------------------------------- /demos/ice.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | 5 | vec2 offset = vec2(noise(vec3(uv * 5., time * 0.1)), 6 | noise(vec3(uv * 5. + vec2(0.3), time * 0.1))) * 7 | pixel * 3.; 8 | offset += vec2(0., -pixel.y * 2.); 9 | offset *= rand(uv.x + uv.y + time); 10 | vec3 prev = getPrevious(uv + offset); 11 | 12 | float face = getFace(uv); 13 | float eye = getEye(uv); 14 | float mouth = getMouth(uv); 15 | 16 | vec2 ed = 6. * pixel; 17 | float edge = dot((getCam(uv) * 4. - getCam(uv + vec2(ed.x, 0)) - 18 | getCam(uv + vec2(-ed.x, 0)) - getCam(uv + vec2(0., ed.y)) - 19 | getCam(uv + vec2(0, -ed.y))) 20 | .rgb, 21 | vec3(0.333)); 22 | 23 | cam = vec3(0.7, .8, 1.0) * dot(cam, vec3(0.333)); 24 | 25 | vec3 color = cam; 26 | 27 | color = (edge + 0.1) * cam * 30.; 28 | 29 | if (eye < 0.8 && mouth < 0.2) { 30 | color = prev * 0.95 + edge * 0.5 + cam * 0.05; 31 | } 32 | 33 | if (face > 0.3) { 34 | color = mix(color, cam, 0.5); 35 | } 36 | 37 | gl_FragColor = vec4(color, 1); 38 | } 39 | -------------------------------------------------------------------------------- /docs/demos/ice.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 cam = getCam(uv); 4 | 5 | vec2 offset = vec2(noise(vec3(uv * 5., time * 0.1)), 6 | noise(vec3(uv * 5. + vec2(0.3), time * 0.1))) * 7 | pixel * 3.; 8 | offset += vec2(0., -pixel.y * 2.); 9 | offset *= rand(uv.x + uv.y + time); 10 | vec3 prev = getPrevious(uv + offset); 11 | 12 | float face = getFace(uv); 13 | float eye = getEye(uv); 14 | float mouth = getMouth(uv); 15 | 16 | vec2 ed = 6. * pixel; 17 | float edge = dot((getCam(uv) * 4. - getCam(uv + vec2(ed.x, 0)) - 18 | getCam(uv + vec2(-ed.x, 0)) - getCam(uv + vec2(0., ed.y)) - 19 | getCam(uv + vec2(0, -ed.y))) 20 | .rgb, 21 | vec3(0.333)); 22 | 23 | cam = vec3(0.7, .8, 1.0) * dot(cam, vec3(0.333)); 24 | 25 | vec3 color = cam; 26 | 27 | color = (edge + 0.1) * cam * 30.; 28 | 29 | if (eye < 0.8 && mouth < 0.2) { 30 | color = prev * 0.95 + edge * 0.5 + cam * 0.05; 31 | } 32 | 33 | if (face > 0.3) { 34 | color = mix(color, cam, 0.5); 35 | } 36 | 37 | gl_FragColor = vec4(color, 1); 38 | } 39 | -------------------------------------------------------------------------------- /demos/barcode.glsl: -------------------------------------------------------------------------------- 1 | // Demo of some face feature stuff; 2 | // leftEye, rightEye, faceCenter. 3 | 4 | float sdCapsule(vec2 p, vec2 a, vec2 b, float r) { 5 | vec2 pa = p - a, ba = b - a; 6 | float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); 7 | return length(pa - ba * h) - r; 8 | } 9 | 10 | float sdCircle(vec2 p, float r) { return length(p) - r; } 11 | void main() { 12 | 13 | vec3 cam = getCam(uv); 14 | vec3 color = cam; 15 | 16 | vec2 eyeVector = leftEye - rightEye; 17 | float occularDistance = length(eyeVector); 18 | 19 | vec2 offset = vec2(occularDistance * 0.5); 20 | 21 | eyeVector = normalize(eyeVector); 22 | vec2 eyeVectorPerp = vec2(-eyeVector.y, eyeVector.x); 23 | 24 | offset *= eyeVector; 25 | 26 | float line = sdCapsule(uv, leftEye, rightEye, 0.03); 27 | if (line < 0.0) { 28 | color = vec3(rand(time + uv.x)); 29 | } 30 | 31 | // line = sdCapsule(uv - (leftEye + rightEye) * 0.5, 32 | // eyeVectorPerp * occularDistance, 33 | // eyeVectorPerp * -occularDistance, 0.005); 34 | // if (line < 0.0) { 35 | // color *= green; 36 | // } 37 | 38 | // if (sdCircle(faceCenter - uv, 0.06) < 0.00) { 39 | // color *= red; 40 | // } 41 | 42 | gl_FragColor = vec4(color, 1); 43 | } 44 | -------------------------------------------------------------------------------- /docs/demos/barcode.glsl: -------------------------------------------------------------------------------- 1 | // Demo of some face feature stuff; 2 | // leftEye, rightEye, faceCenter. 3 | 4 | float sdCapsule(vec2 p, vec2 a, vec2 b, float r) { 5 | vec2 pa = p - a, ba = b - a; 6 | float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); 7 | return length(pa - ba * h) - r; 8 | } 9 | 10 | float sdCircle(vec2 p, float r) { return length(p) - r; } 11 | void main() { 12 | 13 | vec3 cam = getCam(uv); 14 | vec3 color = cam; 15 | 16 | vec2 eyeVector = leftEye - rightEye; 17 | float occularDistance = length(eyeVector); 18 | 19 | vec2 offset = vec2(occularDistance * 0.5); 20 | 21 | eyeVector = normalize(eyeVector); 22 | vec2 eyeVectorPerp = vec2(-eyeVector.y, eyeVector.x); 23 | 24 | offset *= eyeVector; 25 | 26 | float line = sdCapsule(uv, leftEye, rightEye, 0.03); 27 | if (line < 0.0) { 28 | color = vec3(rand(time + uv.x)); 29 | } 30 | 31 | // line = sdCapsule(uv - (leftEye + rightEye) * 0.5, 32 | // eyeVectorPerp * occularDistance, 33 | // eyeVectorPerp * -occularDistance, 0.005); 34 | // if (line < 0.0) { 35 | // color *= green; 36 | // } 37 | 38 | // if (sdCircle(faceCenter - uv, 0.06) < 0.00) { 39 | // color *= red; 40 | // } 41 | 42 | gl_FragColor = vec4(color, 1); 43 | } 44 | -------------------------------------------------------------------------------- /demos/godrays.glsl: -------------------------------------------------------------------------------- 1 | // Ricky Reusser @rreusser 2 | // https://github.com/Erkaman/glsl-godrays/blob/master/example/index.js 3 | vec3 godrays(float density, float weight, float decay, float exposure, 4 | int numSamples, sampler2D occlusionTexture, 5 | vec2 screenSpaceLightPos, vec2 uv) { 6 | vec3 fragColor = vec3(0.0, 0.0, 0.0); 7 | vec2 deltaTextCoord = vec2(uv - screenSpaceLightPos.xy); 8 | vec2 textCoo = uv.xy; 9 | deltaTextCoord *= (1.0 / float(numSamples)) * density; 10 | float illuminationDecay = 1.0; 11 | for (int i = 0; i < 200; i++) { 12 | if (numSamples < i) 13 | break; 14 | textCoo -= deltaTextCoord; 15 | vec3 samp = texture2D(occlusionTexture, textCoo).xyz; 16 | samp.xy -= vec2(0.5, 0.2); 17 | samp.xy = max(samp.xy, 0.); 18 | samp.xy * -4.; 19 | 20 | samp = vec3(dot(samp.xy, samp.xy)); 21 | samp *= illuminationDecay * weight; 22 | fragColor += samp; 23 | illuminationDecay *= decay; 24 | } 25 | fragColor *= vec3(1.0, 0.7, 0.5); 26 | fragColor *= exposure; 27 | return fragColor; 28 | } 29 | 30 | void main() { 31 | vec3 cam = getCam(uv); 32 | vec3 color = cam * 0.5; 33 | color += godrays(1.0, 0.01, 1.0, 4.0, 200, maskTex, vec2(0.5, 0.55), 34 | 0.5 - 0.5 * uv); 35 | gl_FragColor = vec4(color, 1); 36 | } -------------------------------------------------------------------------------- /docs/demos/godrays.glsl: -------------------------------------------------------------------------------- 1 | // Ricky Reusser @rreusser 2 | // https://github.com/Erkaman/glsl-godrays/blob/master/example/index.js 3 | vec3 godrays(float density, float weight, float decay, float exposure, 4 | int numSamples, sampler2D occlusionTexture, 5 | vec2 screenSpaceLightPos, vec2 uv) { 6 | vec3 fragColor = vec3(0.0, 0.0, 0.0); 7 | vec2 deltaTextCoord = vec2(uv - screenSpaceLightPos.xy); 8 | vec2 textCoo = uv.xy; 9 | deltaTextCoord *= (1.0 / float(numSamples)) * density; 10 | float illuminationDecay = 1.0; 11 | for (int i = 0; i < 200; i++) { 12 | if (numSamples < i) 13 | break; 14 | textCoo -= deltaTextCoord; 15 | vec3 samp = texture2D(occlusionTexture, textCoo).xyz; 16 | samp.xy -= vec2(0.5, 0.2); 17 | samp.xy = max(samp.xy, 0.); 18 | samp.xy * -4.; 19 | 20 | samp = vec3(dot(samp.xy, samp.xy)); 21 | samp *= illuminationDecay * weight; 22 | fragColor += samp; 23 | illuminationDecay *= decay; 24 | } 25 | fragColor *= vec3(1.0, 0.7, 0.5); 26 | fragColor *= exposure; 27 | return fragColor; 28 | } 29 | 30 | void main() { 31 | vec3 cam = getCam(uv); 32 | vec3 color = cam * 0.5; 33 | color += godrays(1.0, 0.01, 1.0, 4.0, 200, maskTex, vec2(0.5, 0.55), 34 | 0.5 - 0.5 * uv); 35 | gl_FragColor = vec4(color, 1); 36 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shaderbooth 2 | 3 | Shaderbooth is a browser based framework and editing environment for makeing and sharing sharing indie face filter artworks, Try it out at [shaderbooth.com](shaderbooth.com) 4 | 5 | ![demo](demo.png) 6 | 7 | Framework API provides realtime facial landmark data to a single fragment shader. 8 | Face detection uses TensorFlow.js [Facemesh](https://github.com/tensorflow/tfjs-models/tree/master/facemesh). 9 | 10 | ## Inspired by: 11 | 12 | Olivia Jack's [hydra](https://github.com/ojack/hydra) 13 | 14 | Shawn Lawson's [The_Force](https://github.com/shawnlawson/The_Force) (this tool is where I first learned to write shaders!) 15 | 16 | Myron Krueger's [Videoplace](https://www.youtube.com/watch?v=dqZyZrN3Pl0) 17 | 18 | Community of [Shadertoy](https://shadertoy.com) 19 | 20 | macOS [Photo Booth](https://en.wikipedia.org/wiki/Photo_Booth) 21 | 22 | It's also motivated by a frustration with owned creative ecosystems, like Spark AR instagram filters! 23 | 24 | Todo: 25 | 26 | - localstorage persist (& reset button?) 27 | - revisit error 28 | - codemirror typeahead (anyword-hint) 29 | - 3x3 30 | 31 | backend: 32 | 33 | - browse examples 34 | 35 | features: 36 | 37 | - background subtraction. 38 | - pass in more landmarks to shader. 39 | - GPU based paint.js? 40 | - custom textures 41 | - texture editor 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "face-mesh-shader", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "./node_modules/.bin/shader-reload-cli index.js:bundle.js --dir docs/ --cors -- -t [ brfs ]", 8 | "build": "cp -r ./demos/* ./docs/demos/ && ./node_modules/.bin/browserify -t brfs -t glslify -g uglifyify index.js -o docs/bundle.js" 9 | }, 10 | "author": "maxbittker (maxbittker.github.io)", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@tensorflow-models/facemesh": "0.0.1", 14 | "@tensorflow/tfjs-converter": "^1.7.0", 15 | "@tensorflow/tfjs-core": "^1.7.1", 16 | "airtable": "^0.8.1", 17 | "brfs": "^2.0.2", 18 | "browserify": "^16.5.0", 19 | "codemirror": "^5.52.2", 20 | "convex-hull": "^1.0.3", 21 | "getusermedia": "^2.0.1", 22 | "glsl-color": "^0.0.1", 23 | "glsl-fractal-brownian-noise": "^1.1.0", 24 | "glsl-hsv2rgb": "^1.0.0", 25 | "glsl-luma": "^1.0.1", 26 | "glsl-noise": "^0.0.0", 27 | "glsl-worley": "^1.0.2", 28 | "hull.js": "^1.0.0", 29 | "regl": "github:maxbittker/regl", 30 | "resl": "^1.0.3", 31 | "uglifyify": "^4.0.5", 32 | "webrtc-adapter": "^6.4.8" 33 | }, 34 | "devDependencies": { 35 | "gh-pages": "^1.2.0", 36 | "glslify": "^6.4.1", 37 | "shader-reload-cli": "^1.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demos/cute.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | vec2 offset = pixel * 1.5; 6 | vec3 blur = getCam(uv + vec2(offset.x, 0.)) / 4.; 7 | blur += getCam(uv + vec2(-offset.x, 0.)) / 4.; 8 | blur += getCam(uv + vec2(0., offset.y)) / 4.; 9 | blur += getCam(uv + vec2(0., -offset.y)) / 4.; 10 | 11 | float freckles = clamp(noise(uv * 100.) - 0.6, 0., 1.0); 12 | float frecklePattern = min(length(uv - leftEye + vec2(0.0, 0.2)), 13 | length(uv - rightEye + vec2(0.0, 0.2))); 14 | frecklePattern = .15 - frecklePattern; 15 | frecklePattern = clamp(frecklePattern, 0., 0.5); 16 | freckles *= frecklePattern; 17 | vec2 pos = uv; 18 | 19 | if (getFace(uv) > 0.2) { 20 | color = blur; 21 | color -= freckles * vec3(1.0) * 7.; 22 | // blush: 23 | color += frecklePattern * red * 1.5; 24 | } 25 | 26 | // lashes and pupils darker 27 | if (getEye(uv) > 0.3) { 28 | color = getCam(uv); 29 | if (luma(color) < 0.2) { 30 | color /= 1.5; 31 | } 32 | } 33 | // eyes brighter 34 | if (getEye(uv) > 0.8) { 35 | if (luma(color) > 0.5) { 36 | color *= 1.4; 37 | } 38 | } 39 | if (getMouth(uv) > 0.2 && getMouth(uv) < 0.7) { 40 | color = getCam(uv); 41 | // lipgloss 42 | if (luma(color) > 0.6) { 43 | color *= 1.1; 44 | } 45 | } 46 | 47 | gl_FragColor = vec4(color, 1); 48 | } 49 | -------------------------------------------------------------------------------- /docs/demos/cute.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | vec3 color = getCam(uv); 4 | 5 | vec2 offset = pixel * 1.5; 6 | vec3 blur = getCam(uv + vec2(offset.x, 0.)) / 4.; 7 | blur += getCam(uv + vec2(-offset.x, 0.)) / 4.; 8 | blur += getCam(uv + vec2(0., offset.y)) / 4.; 9 | blur += getCam(uv + vec2(0., -offset.y)) / 4.; 10 | 11 | float freckles = clamp(noise(uv * 100.) - 0.6, 0., 1.0); 12 | float frecklePattern = min(length(uv - leftEye + vec2(0.0, 0.2)), 13 | length(uv - rightEye + vec2(0.0, 0.2))); 14 | frecklePattern = .15 - frecklePattern; 15 | frecklePattern = clamp(frecklePattern, 0., 0.5); 16 | freckles *= frecklePattern; 17 | vec2 pos = uv; 18 | 19 | if (getFace(uv) > 0.2) { 20 | color = blur; 21 | color -= freckles * vec3(1.0) * 7.; 22 | // blush: 23 | color += frecklePattern * red * 1.5; 24 | } 25 | 26 | // lashes and pupils darker 27 | if (getEye(uv) > 0.3) { 28 | color = getCam(uv); 29 | if (luma(color) < 0.2) { 30 | color /= 1.5; 31 | } 32 | } 33 | // eyes brighter 34 | if (getEye(uv) > 0.8) { 35 | if (luma(color) > 0.5) { 36 | color *= 1.4; 37 | } 38 | } 39 | if (getMouth(uv) > 0.2 && getMouth(uv) < 0.7) { 40 | color = getCam(uv); 41 | // lipgloss 42 | if (luma(color) > 0.6) { 43 | color *= 1.1; 44 | } 45 | } 46 | 47 | gl_FragColor = vec4(color, 1); 48 | } 49 | -------------------------------------------------------------------------------- /demos/cabbibo.glsl: -------------------------------------------------------------------------------- 1 | 2 | vec2 modit(vec2 x, vec2 y) { return x - y * floor(x / y); } 3 | 4 | float modit(float x, float y) { return x - y * floor(x / y); } 5 | void main() { 6 | 7 | vec2 pos = vec2(uv.x, uv.y * resolution.y / resolution.x) * 1.3; 8 | vec3 color = getCam(pos); 9 | vec3 camCol = color; 10 | 11 | float hit = 0.; 12 | 13 | vec3 ro = vec3(pos.x, pos.y, 1.); 14 | vec3 rd = normalize(ro - vec3(0.)); 15 | 16 | vec3 faceVec = vec3(faceCenter.x, faceCenter.y + .1, 1.3); 17 | 18 | int hitStep = 0; 19 | color = vec3(1.); 20 | for (int i = 0; i < 20; i++) { 21 | vec3 pos2 = ro + rd * float(i) * .04; 22 | 23 | float fV = length(faceVec - pos2); 24 | 25 | hit = fV * .5 + fbn((pos2 * 4. + vec3(0., 0., time * .3)), 3) * .1 * fV * 26 | fV * fV * 20.; 27 | 28 | if (i == 17) { 29 | if (getFace(pos2.xy * .7) > .5) { 30 | hitStep = i; 31 | color = getCam(pos2.xy * .7); 32 | vec3(1., 0., 0.); 33 | break; 34 | } 35 | } 36 | if (hit > .34) { 37 | color = hsl2rgb(modit((float(i) / 100.) + hit * .01 + 38 | sin(float(i) * 0.1) * .2 + time * .1, 39 | 1.), 40 | .4, 0.4 + float(i) / 40.); 41 | hitStep = i; 42 | 43 | break; 44 | } 45 | } 46 | 47 | if (hitStep == 0) { 48 | color = getPrevious(uv) * .9; 49 | } 50 | 51 | gl_FragColor = vec4(color, 1); 52 | } 53 | -------------------------------------------------------------------------------- /docs/demos/cabbibo.glsl: -------------------------------------------------------------------------------- 1 | 2 | vec2 modit(vec2 x, vec2 y) { return x - y * floor(x / y); } 3 | 4 | float modit(float x, float y) { return x - y * floor(x / y); } 5 | void main() { 6 | 7 | vec2 pos = vec2(uv.x, uv.y * resolution.y / resolution.x) * 1.3; 8 | vec3 color = getCam(pos); 9 | vec3 camCol = color; 10 | 11 | float hit = 0.; 12 | 13 | vec3 ro = vec3(pos.x, pos.y, 1.); 14 | vec3 rd = normalize(ro - vec3(0.)); 15 | 16 | vec3 faceVec = vec3(faceCenter.x, faceCenter.y + .1, 1.3); 17 | 18 | int hitStep = 0; 19 | color = vec3(1.); 20 | for (int i = 0; i < 20; i++) { 21 | vec3 pos2 = ro + rd * float(i) * .04; 22 | 23 | float fV = length(faceVec - pos2); 24 | 25 | hit = fV * .5 + fbn((pos2 * 4. + vec3(0., 0., time * .3)), 3) * .1 * fV * 26 | fV * fV * 20.; 27 | 28 | if (i == 17) { 29 | if (getFace(pos2.xy * .7) > .5) { 30 | hitStep = i; 31 | color = getCam(pos2.xy * .7); 32 | vec3(1., 0., 0.); 33 | break; 34 | } 35 | } 36 | if (hit > .34) { 37 | color = hsl2rgb(modit((float(i) / 100.) + hit * .01 + 38 | sin(float(i) * 0.1) * .2 + time * .1, 39 | 1.), 40 | .4, 0.4 + float(i) / 40.); 41 | hitStep = i; 42 | 43 | break; 44 | } 45 | } 46 | 47 | if (hitStep == 0) { 48 | color = getPrevious(uv) * .9; 49 | } 50 | 51 | gl_FragColor = vec4(color, 1); 52 | } 53 | -------------------------------------------------------------------------------- /demos/starter.glsl: -------------------------------------------------------------------------------- 1 | // Welcome to Shaderbooth! 2 | // ______ __ ___ __ __ 3 | // / __/ / ___ ____/ /__ ____/ _ )___ ___ / /_/ / 4 | // _\ \/ _ \/ _ `/ _ / -_) __/ _ / _ \/ _ \/ __/ _ \ 5 | // /___/_//_/\_,_/\_,_/\__/_/ /____/\___/\___/\__/_//_/ 6 | // 7 | 8 | // This is an interactive editor for making face filters with WebGL. 9 | // The language below is called GLSL, you can edit it to change the effect. 10 | // Press the arrows in the bottom right to see more examples! 11 | 12 | void main() { 13 | 14 | vec3 cam = getCam(uv); 15 | vec3 prev = getPrevious(uv * 1.05 + pixel * 20.); 16 | 17 | float face = getFace(uv); 18 | float eye = getEye(uv); 19 | float mouth = getMouth(uv); 20 | 21 | vec3 color = cam; 22 | 23 | // try uncommenting this line: 24 | // color = getPrevious(uv + (cam.rg-cam.bb)*0.1); 25 | 26 | vec2 offset = uv - faceCenter; 27 | if (face > 0.2) { 28 | color = cam * pink; 29 | color.g += .1*sin((offset.y +offset.x)*100.) ; 30 | // or this one 31 | // color = getPrevious(rotate(uv, 0.1) * 1.1); 32 | } 33 | 34 | // eye black 35 | if (eye > 0.3) { 36 | color += green*.3; 37 | } 38 | if (eye > 0.6) { 39 | color = cam; 40 | } 41 | // lipstick 42 | if (mouth > 0.4) { 43 | color += green*.3; 44 | } 45 | if (mouth > 0.6 ) { 46 | color = cam; 47 | } 48 | if (max(abs(uv.x), abs(uv.y)) > 0.99) { 49 | color = cam; 50 | } 51 | gl_FragColor = vec4(color, 1); 52 | } 53 | -------------------------------------------------------------------------------- /docs/demos/starter.glsl: -------------------------------------------------------------------------------- 1 | // Welcome to Shaderbooth! 2 | // ______ __ ___ __ __ 3 | // / __/ / ___ ____/ /__ ____/ _ )___ ___ / /_/ / 4 | // _\ \/ _ \/ _ `/ _ / -_) __/ _ / _ \/ _ \/ __/ _ \ 5 | // /___/_//_/\_,_/\_,_/\__/_/ /____/\___/\___/\__/_//_/ 6 | // 7 | 8 | // This is an interactive editor for making face filters with WebGL. 9 | // The language below is called GLSL, you can edit it to change the effect. 10 | // Press the arrows in the bottom right to see more examples! 11 | 12 | void main() { 13 | 14 | vec3 cam = getCam(uv); 15 | vec3 prev = getPrevious(uv * 1.05 + pixel * 20.); 16 | 17 | float face = getFace(uv); 18 | float eye = getEye(uv); 19 | float mouth = getMouth(uv); 20 | 21 | vec3 color = cam; 22 | 23 | // try uncommenting this line: 24 | // color = getPrevious(uv + (cam.rg-cam.bb)*0.1); 25 | 26 | vec2 offset = uv - faceCenter; 27 | if (face > 0.2) { 28 | color = cam * pink; 29 | color.g += .1*sin((offset.y +offset.x)*100.) ; 30 | // or this one 31 | // color = getPrevious(rotate(uv, 0.1) * 1.1); 32 | } 33 | 34 | // eye black 35 | if (eye > 0.3) { 36 | color += green*.3; 37 | } 38 | if (eye > 0.6) { 39 | color = cam; 40 | } 41 | // lipstick 42 | if (mouth > 0.4) { 43 | color += green*.3; 44 | } 45 | if (mouth > 0.6 ) { 46 | color = cam; 47 | } 48 | if (max(abs(uv.x), abs(uv.y)) > 0.99) { 49 | color = cam; 50 | } 51 | gl_FragColor = vec4(color, 1); 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Max Bittker 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 | 23 | // 24 | // Description : Array and textureless GLSL 2D/3D/4D simplex 25 | // noise functions. 26 | // Author : Ian McEwan, Ashima Arts. 27 | // Maintainer : stegu 28 | // Lastmod : 20110822 (ijm) 29 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 30 | // Distributed under the MIT License. See LICENSE file. 31 | // https://github.com/ashima/webgl-noise 32 | // https://github.com/stegu/webgl-noise 33 | // 34 | -------------------------------------------------------------------------------- /server/serve.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const bodyParser = require("body-parser"); 5 | const crypto = require("crypto"); 6 | const https = require("https"); 7 | const cors = require("cors"); 8 | 9 | const filePath = path.join(__dirname, "sketches"); 10 | const port = 3002; 11 | 12 | const app = express(); 13 | 14 | app.use(cors()); 15 | app.options("*", cors()); 16 | app.use(bodyParser.text()); 17 | app.use("/static", express.static(filePath)); 18 | 19 | let cacheFiles = []; 20 | app.get("/", (req, res) => { 21 | fs.promises.readdir(filePath).then(files => { 22 | cacheFiles = files; 23 | res.send(JSON.stringify(files)); 24 | }); 25 | }); 26 | 27 | app.post("/upload", (req, res) => { 28 | let id = crypto 29 | .createHash("md5") 30 | .update(req.body.toString()) 31 | .digest("hex") 32 | .slice(0, 5); 33 | fs.promises 34 | .writeFile(path.join(filePath, id), req.body.toString(), { flag: "wx" }) 35 | .catch(err => { 36 | console.log(err); 37 | }); 38 | res.send(JSON.stringify({ id })); 39 | }); 40 | 41 | const creds = { 42 | key: fs.readFileSync( 43 | "/etc/letsencrypt/live/api.shaderbooth.com/fullchain.pem", 44 | "utf8" 45 | ), 46 | cert: fs.readFileSync( 47 | "/etc/letsencrypt/live/api.shaderbooth.com/privkey.pem", 48 | "utf8" 49 | ), 50 | ca: fs.readFileSync( 51 | "/etc/letsencrypt/live/api.shaderbooth.com/chain.pem", 52 | "utf8" 53 | ) 54 | }; 55 | console.log(creds); 56 | https 57 | .createServer(creds, app) 58 | .listen(port, () => console.log(`Example app listening on port ${port}!`)); 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /src/loadingShader.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform float tick; 3 | uniform float time; 4 | uniform float targetAspect; 5 | uniform bool hasFace; 6 | uniform vec2 resolution; 7 | uniform sampler2D previousTex; 8 | uniform sampler2D camTex; 9 | uniform vec2 videoResolution; 10 | uniform vec2 scaledVideoResolution; 11 | varying vec2 uv; 12 | 13 | vec2 pixel = 1.0 / resolution; 14 | 15 | vec3 getCam(vec2 pos) { 16 | float videoAspect = videoResolution.x / videoResolution.y; 17 | 18 | vec2 uvAspect = vec2(pos.x * targetAspect / videoAspect, pos.y); 19 | if ((targetAspect) < (videoResolution.x / videoResolution.y)) { 20 | uvAspect = vec2(pos.x, pos.y * videoAspect / targetAspect); 21 | } 22 | vec2 webcamCoord = (uvAspect) / 2.0 + vec2(0.5); 23 | vec2 flipwcord = vec2(1.) - webcamCoord; 24 | 25 | float howFar = 1.0; 26 | if (uvAspect.x <= -1.0 || uvAspect.x > 1.0 || uvAspect.y < -1.0 || 27 | uvAspect.y > 1.0) { 28 | howFar = max(abs(uvAspect.x), abs(uvAspect.y)) - 1.0; 29 | howFar = 2.0 + float(int(howFar * 5.)); 30 | vec2 towardsCenter = vec2(0., 0.0); 31 | if (uvAspect.x < -1.) { 32 | towardsCenter = vec2(-1.0, 0.0); 33 | } else if (uvAspect.x > 1.) { 34 | towardsCenter = vec2(1.0, 0.0); 35 | } else if (uvAspect.y > 1.) { 36 | towardsCenter = vec2(0.0, 1.0); 37 | } else if (uvAspect.y < -1.) { 38 | towardsCenter = vec2(0.0, -1.0); 39 | } 40 | flipwcord += towardsCenter * (1. / 8.) * howFar; 41 | float blurRadius = 4.0 + howFar; 42 | 43 | float blurAngle = sin((flipwcord.x + flipwcord.y) * 555.534) * 3.14 * 2.; 44 | vec2 blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 45 | vec3 color = texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 46 | 47 | blurAngle += 2.0; 48 | blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 49 | color += texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 50 | 51 | blurAngle += 2.0; 52 | blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 53 | color += texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 54 | 55 | return color; 56 | } 57 | 58 | return texture2D(camTex, flipwcord).rgb; 59 | } 60 | 61 | void main() { 62 | vec2 uvN = (uv * 0.5) + vec2(0.5); 63 | 64 | vec2 backCoord = uv * (1.0 + 0.01); 65 | backCoord = (backCoord / 2.0) + vec2(0.5); 66 | vec3 backBufferColor = texture2D(previousTex, backCoord).rgb; 67 | vec3 color = getCam(uv); 68 | 69 | if (!hasFace) { 70 | float scanLine = mod(time * 0.5, 1.0); 71 | if (abs(uvN.x - scanLine) < pixel.x * 1.) { 72 | color += vec3(0., 1.0, 0.) * 73 | (0.5 + max(sin(uvN.y * 200. + time * 2.0), 0.0) * 0.5); 74 | } 75 | color.g = color.g * 0.5 + backBufferColor.g * 0.5; 76 | } 77 | gl_FragColor = vec4(color, 1); 78 | } 79 | -------------------------------------------------------------------------------- /docs/lucario.css: -------------------------------------------------------------------------------- 1 | /* 2 | Name: lucario 3 | Author: Raphael Amorim 4 | 5 | Original Lucario color scheme (https://github.com/raphamorim/lucario) 6 | */ 7 | 8 | .cm-s-lucario.CodeMirror, 9 | .cm-s-lucario .CodeMirror-gutters { 10 | background-color: rgba(0, 0, 0, 0) !important; 11 | color: #f8f8f2 !important; 12 | font-size: 15px; 13 | 14 | border: none; 15 | } 16 | span[role="presentation"] { 17 | background-color: rgba(0, 0, 0, 0.24); 18 | filter: drop-shadow(1px 1px 3px black); 19 | border-radius: 1px; 20 | } 21 | .cm-matchhighlight { 22 | /* background: #b69d2cff; */ 23 | text-decoration: underline #e6c845; 24 | } 25 | .cm-s-lucario .CodeMirror-gutters { 26 | color: #2b3e50; 27 | } 28 | .cm-custom-error{ 29 | background-color: rgba(218, 40, 40, 0.801) !important; 30 | } 31 | .lint-error{ 32 | /* background-color: rgba(218, 40, 40, 0.801) !important; */ 33 | } 34 | .cm-s-lucario .CodeMirror-cursor { 35 | border-left: solid thin #e6c845; 36 | filter: drop-shadow(1px 1px 3px rgb(245, 245, 245)); 37 | } 38 | .cm-s-lucario .CodeMirror-linenumber { 39 | color: #f8f8f2; 40 | } 41 | .cm-s-lucario .CodeMirror-selected { 42 | /* background: #243443; */ 43 | background: rgb(182, 127, 44); 44 | } 45 | .cm-s-lucario .CodeMirror-line::selection, 46 | .cm-s-lucario .CodeMirror-line > span::selection, 47 | .cm-s-lucario .CodeMirror-line > span > span::selection { 48 | background: #243443; 49 | } 50 | .cm-s-lucario .CodeMirror-line::-moz-selection, 51 | .cm-s-lucario .CodeMirror-line > span::-moz-selection, 52 | .cm-s-lucario .CodeMirror-line > span > span::-moz-selection { 53 | background: #243443; 54 | } 55 | .cm-s-lucario span.cm-comment { 56 | color: #5c98cd; 57 | } 58 | .cm-s-lucario span.cm-string, 59 | .cm-s-lucario span.cm-string-2 { 60 | color: #e6db74; 61 | } 62 | .cm-s-lucario span.cm-number { 63 | color: #ca94ff; 64 | } 65 | .cm-s-lucario span.cm-variable { 66 | color: #f8f8f2; 67 | } 68 | .cm-s-lucario span.cm-variable-2 { 69 | color: #f8f8f2; 70 | } 71 | .cm-s-lucario span.cm-def { 72 | color: #72c05d; 73 | } 74 | .cm-s-lucario span.cm-operator { 75 | color: #66d9ef; 76 | } 77 | .cm-s-lucario span.cm-keyword { 78 | color: #ff6541; 79 | } 80 | .cm-s-lucario span.cm-atom { 81 | color: #bd93f9; 82 | } 83 | .cm-s-lucario span.cm-meta { 84 | color: #f8f8f2; 85 | } 86 | .cm-s-lucario span.cm-tag { 87 | color: #ff6541; 88 | } 89 | .cm-s-lucario span.cm-attribute { 90 | color: #66d9ef; 91 | } 92 | .cm-s-lucario span.cm-qualifier { 93 | color: #72c05d; 94 | } 95 | .cm-s-lucario span.cm-property { 96 | color: #f8f8f2; 97 | } 98 | .cm-s-lucario span.cm-builtin { 99 | color: #72c05d; 100 | } 101 | .cm-s-lucario span.cm-variable-3, 102 | .cm-s-lucario span.cm-type { 103 | color: #ffb86c; 104 | } 105 | 106 | .cm-s-lucario .CodeMirror-activeline-background { 107 | background: #243443; 108 | } 109 | .cm-s-lucario .CodeMirror-matchingbracket { 110 | text-decoration: underline; 111 | color: white !important; 112 | } 113 | -------------------------------------------------------------------------------- /src/setup-facemesh.js: -------------------------------------------------------------------------------- 1 | let facemesh = require("@tensorflow-models/facemesh"); 2 | let tf = require("@tensorflow/tfjs-core"); 3 | 4 | let loader = document.getElementById("loading"); 5 | let model; 6 | let video = null; 7 | async function loadModel() { 8 | // Load the MediaPipe facemesh model. 9 | model = await facemesh.load({ maxFaces: 1 }); 10 | loader.style = "display:none"; 11 | } 12 | let keypoints; 13 | let dirty = false; 14 | async function predictionLoop() { 15 | if (!model || !video) { 16 | window.requestAnimationFrame(predictionLoop); 17 | 18 | return null; 19 | } 20 | 21 | // Pass in a video stream (or an image, canvas, or 3D tensor) to obtain an 22 | // array of detected faces from the MediaPipe graph. 23 | const predictions = await model.estimateFaces(video); 24 | // console.log(tf.backend()); 25 | // console.log(model); 26 | // console.log(facemesh); 27 | if (predictions.length > 0) { 28 | for (let i = 0; i < predictions.length; i++) { 29 | keypoints = predictions[i].annotations; 30 | dirty = true; 31 | // console.log(keypoints); 32 | // keypoints = predictions[i].scaledMesh; 33 | } 34 | } 35 | window.requestAnimationFrame(predictionLoop); 36 | } 37 | function getKeyPoints() { 38 | if (dirty) { 39 | dirty = false; 40 | return keypoints; 41 | } else { 42 | // console.log("saved work"); 43 | return false; 44 | } 45 | } 46 | loadModel(); 47 | function setupWebcam(options) { 48 | const regl = options.regl; 49 | 50 | function startup() { 51 | video = document.getElementById("video"); 52 | let startbutton = document.getElementById("start"); 53 | let paint = document.getElementById("paint"); 54 | let target = document.getElementById("target"); 55 | var trackingStarted = false; 56 | 57 | function tryGetUserMedia() { 58 | navigator.mediaDevices 59 | // .getDisplayMedia({ 60 | .getUserMedia({ 61 | video: true, 62 | audio: false 63 | }) 64 | .then(gumSuccess) 65 | .catch(e => { 66 | console.log("initial gum failed"); 67 | }); 68 | // video.play(); 69 | startbutton.hidden = true; 70 | } 71 | 72 | tryGetUserMedia(); 73 | 74 | startbutton.onclick = function() { 75 | console.log("play!"); 76 | tryGetUserMedia(); 77 | // startVideo(); 78 | }; 79 | 80 | function gumSuccess(stream) { 81 | loader.innerText = "Loading Model..."; 82 | if ("srcObject" in video) { 83 | video.srcObject = stream; 84 | } else { 85 | video.src = window.URL && window.URL.createObjectURL(stream); 86 | } 87 | video.onloadedmetadata = function() { 88 | console.log("metadata loaded"); 89 | const webcam = regl.texture(video); 90 | 91 | const { videoWidth, videoHeight } = video; 92 | 93 | var w = videoWidth; 94 | var h = videoHeight; 95 | video.height = h; 96 | video.width = w; 97 | paint.height = h; 98 | paint.width = w; 99 | target.height = h; 100 | target.width = w; 101 | predictionLoop(); 102 | 103 | regl.frame(() => webcam.subimage(video)); 104 | options.done(webcam, { 105 | videoWidth, 106 | videoHeight, 107 | getKeyPoints 108 | }); 109 | }; 110 | } 111 | // function adjustVideoProportions() { 112 | // // resize overlay and video if proportions of video are not 4:3 113 | // // keep same height, just change width 114 | // debugger 115 | // var proportion = video.videoWidth/video.videoHeight; 116 | // video_width = Math.round(video_height * proportion); 117 | // video.width = video_width; 118 | // } 119 | video.onresize = function() { 120 | // adjustVideoProportions(); 121 | }; 122 | video.addEventListener( 123 | "canplay", 124 | function(ev) { 125 | video.play(); 126 | }, 127 | false 128 | ); 129 | } 130 | 131 | window.onload = startup; 132 | } 133 | 134 | module.exports = { setupWebcam }; 135 | -------------------------------------------------------------------------------- /demos/dither.glsl: -------------------------------------------------------------------------------- 1 | // ditherface by max 2 | 3 | // 8x8 dither 4 | // https://github.com/hughsk/glsl-dither/blob/master/8x8.glsl 5 | float dither8x8(vec2 position, float brightness) { 6 | int x = int(mod(position.x, 8.0)); 7 | int y = int(mod(position.y, 8.0)); 8 | int index = x + y * 8; 9 | float limit = 0.0; 10 | 11 | if (x < 8) { 12 | if (index == 0) 13 | limit = 0.015625; 14 | if (index == 1) 15 | limit = 0.515625; 16 | if (index == 2) 17 | limit = 0.140625; 18 | if (index == 3) 19 | limit = 0.640625; 20 | if (index == 4) 21 | limit = 0.046875; 22 | if (index == 5) 23 | limit = 0.546875; 24 | if (index == 6) 25 | limit = 0.171875; 26 | if (index == 7) 27 | limit = 0.671875; 28 | if (index == 8) 29 | limit = 0.765625; 30 | if (index == 9) 31 | limit = 0.265625; 32 | if (index == 10) 33 | limit = 0.890625; 34 | if (index == 11) 35 | limit = 0.390625; 36 | if (index == 12) 37 | limit = 0.796875; 38 | if (index == 13) 39 | limit = 0.296875; 40 | if (index == 14) 41 | limit = 0.921875; 42 | if (index == 15) 43 | limit = 0.421875; 44 | if (index == 16) 45 | limit = 0.203125; 46 | if (index == 17) 47 | limit = 0.703125; 48 | if (index == 18) 49 | limit = 0.078125; 50 | if (index == 19) 51 | limit = 0.578125; 52 | if (index == 20) 53 | limit = 0.234375; 54 | if (index == 21) 55 | limit = 0.734375; 56 | if (index == 22) 57 | limit = 0.109375; 58 | if (index == 23) 59 | limit = 0.609375; 60 | if (index == 24) 61 | limit = 0.953125; 62 | if (index == 25) 63 | limit = 0.453125; 64 | if (index == 26) 65 | limit = 0.828125; 66 | if (index == 27) 67 | limit = 0.328125; 68 | if (index == 28) 69 | limit = 0.984375; 70 | if (index == 29) 71 | limit = 0.484375; 72 | if (index == 30) 73 | limit = 0.859375; 74 | if (index == 31) 75 | limit = 0.359375; 76 | if (index == 32) 77 | limit = 0.0625; 78 | if (index == 33) 79 | limit = 0.5625; 80 | if (index == 34) 81 | limit = 0.1875; 82 | if (index == 35) 83 | limit = 0.6875; 84 | if (index == 36) 85 | limit = 0.03125; 86 | if (index == 37) 87 | limit = 0.53125; 88 | if (index == 38) 89 | limit = 0.15625; 90 | if (index == 39) 91 | limit = 0.65625; 92 | if (index == 40) 93 | limit = 0.8125; 94 | if (index == 41) 95 | limit = 0.3125; 96 | if (index == 42) 97 | limit = 0.9375; 98 | if (index == 43) 99 | limit = 0.4375; 100 | if (index == 44) 101 | limit = 0.78125; 102 | if (index == 45) 103 | limit = 0.28125; 104 | if (index == 46) 105 | limit = 0.90625; 106 | if (index == 47) 107 | limit = 0.40625; 108 | if (index == 48) 109 | limit = 0.25; 110 | if (index == 49) 111 | limit = 0.75; 112 | if (index == 50) 113 | limit = 0.125; 114 | if (index == 51) 115 | limit = 0.625; 116 | if (index == 52) 117 | limit = 0.21875; 118 | if (index == 53) 119 | limit = 0.71875; 120 | if (index == 54) 121 | limit = 0.09375; 122 | if (index == 55) 123 | limit = 0.59375; 124 | if (index == 56) 125 | limit = 1.0; 126 | if (index == 57) 127 | limit = 0.5; 128 | if (index == 58) 129 | limit = 0.875; 130 | if (index == 59) 131 | limit = 0.375; 132 | if (index == 60) 133 | limit = 0.96875; 134 | if (index == 61) 135 | limit = 0.46875; 136 | if (index == 62) 137 | limit = 0.84375; 138 | if (index == 63) 139 | limit = 0.34375; 140 | } 141 | 142 | return brightness < limit ? 0.0 : 1.0; 143 | } 144 | 145 | void main() { 146 | 147 | vec3 cam = getCam(uv); 148 | vec3 prev = getPrevious(uv); 149 | float face = getFace(uv); 150 | 151 | vec3 color = cam; 152 | 153 | if (face > 0.2) { 154 | cam = white - cam * 1.1; 155 | } 156 | 157 | color = dither8x8(uv * resolution / 2., luma(cam)) * 158 | hsl2rgb(mod(time * 0.1, 1.0), 0.9, 0.9); 159 | 160 | gl_FragColor = vec4(color, 1); 161 | } 162 | -------------------------------------------------------------------------------- /docs/demos/dither.glsl: -------------------------------------------------------------------------------- 1 | // ditherface by max 2 | 3 | // 8x8 dither 4 | // https://github.com/hughsk/glsl-dither/blob/master/8x8.glsl 5 | float dither8x8(vec2 position, float brightness) { 6 | int x = int(mod(position.x, 8.0)); 7 | int y = int(mod(position.y, 8.0)); 8 | int index = x + y * 8; 9 | float limit = 0.0; 10 | 11 | if (x < 8) { 12 | if (index == 0) 13 | limit = 0.015625; 14 | if (index == 1) 15 | limit = 0.515625; 16 | if (index == 2) 17 | limit = 0.140625; 18 | if (index == 3) 19 | limit = 0.640625; 20 | if (index == 4) 21 | limit = 0.046875; 22 | if (index == 5) 23 | limit = 0.546875; 24 | if (index == 6) 25 | limit = 0.171875; 26 | if (index == 7) 27 | limit = 0.671875; 28 | if (index == 8) 29 | limit = 0.765625; 30 | if (index == 9) 31 | limit = 0.265625; 32 | if (index == 10) 33 | limit = 0.890625; 34 | if (index == 11) 35 | limit = 0.390625; 36 | if (index == 12) 37 | limit = 0.796875; 38 | if (index == 13) 39 | limit = 0.296875; 40 | if (index == 14) 41 | limit = 0.921875; 42 | if (index == 15) 43 | limit = 0.421875; 44 | if (index == 16) 45 | limit = 0.203125; 46 | if (index == 17) 47 | limit = 0.703125; 48 | if (index == 18) 49 | limit = 0.078125; 50 | if (index == 19) 51 | limit = 0.578125; 52 | if (index == 20) 53 | limit = 0.234375; 54 | if (index == 21) 55 | limit = 0.734375; 56 | if (index == 22) 57 | limit = 0.109375; 58 | if (index == 23) 59 | limit = 0.609375; 60 | if (index == 24) 61 | limit = 0.953125; 62 | if (index == 25) 63 | limit = 0.453125; 64 | if (index == 26) 65 | limit = 0.828125; 66 | if (index == 27) 67 | limit = 0.328125; 68 | if (index == 28) 69 | limit = 0.984375; 70 | if (index == 29) 71 | limit = 0.484375; 72 | if (index == 30) 73 | limit = 0.859375; 74 | if (index == 31) 75 | limit = 0.359375; 76 | if (index == 32) 77 | limit = 0.0625; 78 | if (index == 33) 79 | limit = 0.5625; 80 | if (index == 34) 81 | limit = 0.1875; 82 | if (index == 35) 83 | limit = 0.6875; 84 | if (index == 36) 85 | limit = 0.03125; 86 | if (index == 37) 87 | limit = 0.53125; 88 | if (index == 38) 89 | limit = 0.15625; 90 | if (index == 39) 91 | limit = 0.65625; 92 | if (index == 40) 93 | limit = 0.8125; 94 | if (index == 41) 95 | limit = 0.3125; 96 | if (index == 42) 97 | limit = 0.9375; 98 | if (index == 43) 99 | limit = 0.4375; 100 | if (index == 44) 101 | limit = 0.78125; 102 | if (index == 45) 103 | limit = 0.28125; 104 | if (index == 46) 105 | limit = 0.90625; 106 | if (index == 47) 107 | limit = 0.40625; 108 | if (index == 48) 109 | limit = 0.25; 110 | if (index == 49) 111 | limit = 0.75; 112 | if (index == 50) 113 | limit = 0.125; 114 | if (index == 51) 115 | limit = 0.625; 116 | if (index == 52) 117 | limit = 0.21875; 118 | if (index == 53) 119 | limit = 0.71875; 120 | if (index == 54) 121 | limit = 0.09375; 122 | if (index == 55) 123 | limit = 0.59375; 124 | if (index == 56) 125 | limit = 1.0; 126 | if (index == 57) 127 | limit = 0.5; 128 | if (index == 58) 129 | limit = 0.875; 130 | if (index == 59) 131 | limit = 0.375; 132 | if (index == 60) 133 | limit = 0.96875; 134 | if (index == 61) 135 | limit = 0.46875; 136 | if (index == 62) 137 | limit = 0.84375; 138 | if (index == 63) 139 | limit = 0.34375; 140 | } 141 | 142 | return brightness < limit ? 0.0 : 1.0; 143 | } 144 | 145 | void main() { 146 | 147 | vec3 cam = getCam(uv); 148 | vec3 prev = getPrevious(uv); 149 | float face = getFace(uv); 150 | 151 | vec3 color = cam; 152 | 153 | if (face > 0.2) { 154 | cam = white - cam * 1.1; 155 | } 156 | 157 | color = dither8x8(uv * resolution / 2., luma(cam)) * 158 | hsl2rgb(mod(time * 0.1, 1.0), 0.9, 0.9); 159 | 160 | gl_FragColor = vec4(color, 1); 161 | } 162 | -------------------------------------------------------------------------------- /src/paint.js: -------------------------------------------------------------------------------- 1 | // let { TRIANGULATION } = require("./triangulation"); 2 | let hull = require("hull.js"); 3 | let canvas = document.getElementById("paint"); 4 | 5 | ctx = canvas.getContext("2d"); 6 | ctx.translate(canvas.width, 0); 7 | ctx.scale(-1, 1); 8 | function averagePoints(points) { 9 | let x = 0; 10 | let y = 0; 11 | for (let i = 0; i < points.length; i++) { 12 | x += points[i][0]; 13 | y += points[i][1]; 14 | } 15 | x /= points.length; 16 | y /= points.length; 17 | return [x, y]; 18 | } 19 | function drawShape(points) { 20 | ctx.beginPath(); 21 | 22 | for (let i = 0; i < points.length; i++) { 23 | const x = points[i][0]; 24 | const y = points[i][1]; 25 | 26 | if (i == 0) { 27 | ctx.moveTo(x, y); 28 | } else { 29 | ctx.lineTo(x, y); 30 | } 31 | } 32 | 33 | ctx.closePath(); 34 | ctx.stroke(); 35 | ctx.fill(); 36 | } 37 | function paintFace(annotations) { 38 | ctx.clearRect(0, 0, canvas.width, canvas.height); 39 | ctx.lineCap = "round"; 40 | 41 | let silhouette = annotations["silhouette"]; 42 | let convexsilhouette = hull(silhouette, 1000); 43 | ctx.fillStyle = `#008`; 44 | ctx.strokeStyle = `#004`; 45 | ctx.lineWidth = 2; 46 | drawShape(convexsilhouette); 47 | ctx.lineWidth = 0; 48 | ctx.strokeStyle = "rgba(1, 1, 1, 0)"; 49 | 50 | let leftEyebrow = [ 51 | ...annotations["leftEyebrowLower"], 52 | ...annotations["leftEyebrowUpper"] 53 | ]; 54 | 55 | let rightEyebrow = [ 56 | ...annotations["rightEyebrowLower"], 57 | ...annotations["rightEyebrowUpper"] 58 | ]; 59 | 60 | ctx.fillStyle = `#048`; 61 | drawShape(hull(leftEyebrow, 20)); 62 | drawShape(hull(rightEyebrow, 20)); 63 | 64 | let leftEye2 = [ 65 | ...annotations["leftEyeLower2"], 66 | ...annotations["leftEyeUpper2"] 67 | ]; 68 | 69 | let rightEye2 = [ 70 | ...annotations["rightEyeLower2"], 71 | ...annotations["rightEyeUpper2"] 72 | ]; 73 | ctx.fillStyle = `#088`; 74 | drawShape(hull(leftEye2, 1000)); 75 | drawShape(hull(rightEye2, 1000)); 76 | 77 | let leftEye1 = [ 78 | ...annotations["leftEyeLower1"], 79 | ...annotations["leftEyeUpper1"] 80 | ]; 81 | 82 | let rightEye1 = [ 83 | ...annotations["rightEyeLower1"], 84 | ...annotations["rightEyeUpper1"] 85 | ]; 86 | ctx.fillStyle = `#0C8`; 87 | drawShape(hull(leftEye1, 1000)); 88 | drawShape(hull(rightEye1, 1000)); 89 | 90 | let leftEye0 = [ 91 | ...annotations["leftEyeLower0"], 92 | ...annotations["leftEyeUpper0"] 93 | ]; 94 | 95 | let rightEye0 = [ 96 | ...annotations["rightEyeLower0"], 97 | ...annotations["rightEyeUpper0"] 98 | ]; 99 | ctx.fillStyle = `#0f8`; 100 | drawShape(hull(leftEye0, 1000)); 101 | drawShape(hull(rightEye0, 1000)); 102 | 103 | window.leftEye = averagePoints(leftEye0); 104 | window.rightEye = averagePoints(rightEye0); 105 | 106 | let outerMouth = [ 107 | ...annotations["lipsUpperOuter"], 108 | ...annotations["lipsLowerOuter"] 109 | ]; 110 | ctx.fillStyle = `#808`; 111 | let outerMouthHull = hull(outerMouth, 1000); 112 | drawShape(outerMouthHull); 113 | 114 | let innerMouth = [ 115 | ...annotations["lipsUpperInner"], 116 | ...annotations["lipsLowerInner"] 117 | ]; 118 | ctx.fillStyle = `#F08`; 119 | let innerMouthHull = hull(innerMouth, 1000); 120 | drawShape(innerMouthHull); 121 | 122 | let nosePath = [ 123 | ...annotations["noseBottom"], 124 | ...annotations["noseTip"], 125 | ...annotations["noseLeftCorner"], 126 | ...annotations["midwayBetweenEyes"], 127 | ...annotations["noseRightCorner"] 128 | ]; 129 | let convexNosePath = hull(nosePath, 1000); 130 | ctx.fillStyle = `#00F`; 131 | ctx.strokeStyle = `#00C`; 132 | ctx.lineWidth = 2; 133 | drawShape(convexNosePath); 134 | 135 | return ctx; 136 | } 137 | module.exports = { paintFace }; 138 | 139 | /* 140 | cheekStuff(){ 141 | 142 | let [x1, y1, z1] = annotations["leftCheek"][0]; 143 | [x2, y2, z2] = annotations["rightCheek"][0]; 144 | let cheekDistance = Math.sqrt( 145 | Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2) 146 | ); 147 | 148 | ctx.beginPath(); 149 | ctx.lineWidth = 0.5; 150 | ctx.arc(x1, y1, cheekDistance * 0.05 , 0, 2 * Math.PI); 151 | ctx.fill(); 152 | 153 | ctx.beginPath(); 154 | ctx.lineWidth = 0.5; 155 | ctx.arc(x2, y2, cheekDistance * 0.05 , 0, 2 * Math.PI); 156 | ctx.fill(); 157 | }*/ 158 | -------------------------------------------------------------------------------- /src/editor.js: -------------------------------------------------------------------------------- 1 | // adapted from ojack/hyrdra 's editor.js 2 | 3 | /* eslint-disable no-eval */ 4 | var CodeMirror = require("codemirror/lib/codemirror"); 5 | require("codemirror/mode/clike/clike"); 6 | // require("codemirror/addon/hint/javascript-hint"); 7 | require("codemirror/addon/edit/closebrackets"); 8 | require("codemirror/addon/hint/show-hint"); 9 | require("codemirror/addon/search/match-highlighter"); 10 | require("codemirror/addon/selection/mark-selection"); 11 | require("codemirror/addon/comment/comment"); 12 | 13 | var isShowing = true; 14 | 15 | var EditorClass = function() { 16 | console.log("*** Editor class created"); 17 | var self = this; 18 | 19 | var container = document.createElement("div"); 20 | container.setAttribute("id", "editor-container"); 21 | container.classList.add("fade"); 22 | var el = document.createElement("TEXTAREA"); 23 | document.body.appendChild(container); 24 | container.appendChild(el); 25 | 26 | this.cm = CodeMirror.fromTextArea(el, { 27 | theme: "lucario", 28 | value: "hello", 29 | mode: { name: "x-shader/x-fragment", globalVars: true }, 30 | 31 | lineWrapping: true, 32 | styleSelectedText: true, 33 | autoCloseBrackets: true, 34 | showCursorWhenSelecting: true, 35 | highlightSelectionMatches: true 36 | }); 37 | this.cm.setOption("extraKeys", { 38 | "Ctrl-/": () => this.cm.execCommand("toggleComment") 39 | }); 40 | this.cm.refresh(); 41 | 42 | this.show(); 43 | // TO DO: add show code param 44 | let searchParams = new URLSearchParams(window.location.search); 45 | let showCode = searchParams.get("show-code"); 46 | 47 | if (showCode == "false") { 48 | console.log("not showing code"); 49 | var l = document.getElementsByClassName("CodeMirror-scroll")[0]; 50 | l.style.display = "none"; 51 | // self.logElement.style.display = 'none' 52 | isShowing = false; 53 | } 54 | //} 55 | }; 56 | 57 | EditorClass.prototype.clear = function() { 58 | this.cm.setValue( 59 | '\n \n // Type some code on a new line (such as "osc().out()"), and press CTRL+shift+enter' 60 | ); 61 | }; 62 | 63 | EditorClass.prototype.setValue = function(val) { 64 | this.cm.setValue(val); 65 | }; 66 | 67 | EditorClass.prototype.getValue = function() { 68 | return this.cm.getValue(); 69 | }; 70 | 71 | EditorClass.prototype.hide = function() { 72 | var l = document.getElementsByClassName("CodeMirror-scroll")[0]; 73 | var m = document.getElementById("modal-header"); 74 | l.style.opacity = 0; 75 | // this.logElement.style.opacity = 0 76 | m.style.opacity = 0; 77 | this.isShowing = false; 78 | }; 79 | 80 | EditorClass.prototype.show = function() { 81 | var l = document.getElementsByClassName("CodeMirror-scroll")[0]; 82 | var m = document.getElementById("modal-header"); 83 | l.style.opacity = 1; 84 | m.style.opacity = 1; 85 | // this.logElement.style.opacity = 1 86 | this.isShowing = true; 87 | }; 88 | 89 | EditorClass.prototype.toggle = function() { 90 | if (this.isShowing) { 91 | this.hide(); 92 | } else { 93 | this.show(); 94 | } 95 | }; 96 | 97 | EditorClass.prototype.getLine = function() { 98 | var c = this.cm.getCursor(); 99 | var s = this.cm.getLine(c.line); 100 | // this.cm.markText({line: c.line, ch:0}, {line: c.line+1, ch:0}, {className: 'styled-background'}) 101 | this.flashCode({ line: c.line, ch: 0 }, { line: c.line + 1, ch: 0 }); 102 | return s; 103 | }; 104 | 105 | EditorClass.prototype.flashCode = function(start, end) { 106 | var marker = this.cm.markText(start, end, { className: "styled-background" }); 107 | setTimeout(() => marker.clear(), 300); 108 | }; 109 | 110 | EditorClass.prototype.getCurrentBlock = function() { 111 | // thanks to graham wakefield + gibber 112 | var editor = this.cm; 113 | var pos = editor.getCursor(); 114 | var startline = pos.line; 115 | var endline = pos.line; 116 | while (startline > 0 && editor.getLine(startline) !== "") { 117 | startline--; 118 | } 119 | while (endline < editor.lineCount() && editor.getLine(endline) !== "") { 120 | endline++; 121 | } 122 | var pos1 = { 123 | line: startline, 124 | ch: 0 125 | }; 126 | var pos2 = { 127 | line: endline, 128 | ch: 0 129 | }; 130 | var str = editor.getRange(pos1, pos2); 131 | 132 | this.flashCode(pos1, pos2); 133 | 134 | return { 135 | start: pos1, 136 | end: pos2, 137 | text: str 138 | }; 139 | }; 140 | 141 | module.exports = EditorClass; 142 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shaderbooth 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 53 | 71 |
72 | 81 |
82 | 83 |
84 | 95 | 100 | 101 | 102 |
103 |
104 | Starting Camera Feed... 105 |
106 | Make sure you're granting camera permissions. 107 |
108 | 109 | If you're on iOS, switch to the safari browser. 110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 123 | 124 | -------------------------------------------------------------------------------- /demos/rayblob.glsl: -------------------------------------------------------------------------------- 1 | 2 | // diffuse is the softer part of the light 3 | float orenNayarDiffuse(vec3 lightDirection, vec3 viewDirection, 4 | vec3 surfaceNormal, float roughness, float albedo) { 5 | 6 | float LdotV = dot(lightDirection, viewDirection); 7 | float NdotL = dot(lightDirection, surfaceNormal); 8 | float NdotV = dot(surfaceNormal, viewDirection); 9 | 10 | float s = LdotV - NdotL * NdotV; 11 | float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); 12 | 13 | float sigma2 = roughness * roughness; 14 | float A = 1.0 + sigma2 * (albedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33)); 15 | float B = 0.45 * sigma2 / (sigma2 + 0.09); 16 | 17 | return albedo * max(0.0, NdotL) * (A + B * s / t) / PI; 18 | } 19 | 20 | // specular means the shiny parts 21 | float gaussianSpecular(vec3 lightDirection, vec3 viewDirection, 22 | vec3 surfaceNormal, float shininess) { 23 | vec3 H = normalize(lightDirection + viewDirection); 24 | float theta = acos(dot(H, surfaceNormal)); 25 | float w = theta / shininess; 26 | return exp(-w * w); 27 | } 28 | 29 | // Define some constants 30 | const int steps = 128; // This is the maximum amount a ray can march. 31 | const float smallNumber = 0.001; 32 | const float maxDist = 10.; // This is the maximum distance a ray can travel. 33 | 34 | float scene(vec3 position) { 35 | // So this is different from the normal sphere equation in that I am 36 | // splitting the position into it's three different parts 37 | // and adding a 10th of a cos wave to the x position so it oscillates left 38 | // to right and a (positive) sin wave to the z position 39 | // so it will go back and forth. 40 | 41 | float radius = 1.9; 42 | 43 | vec2 eyeVector = leftEye - rightEye; 44 | float occularDistance = length(eyeVector); 45 | 46 | radius += noise(position * 1.7) * 0.58; 47 | radius *= occularDistance; 48 | 49 | float sphere = length(vec3(position.x + cos(time) / 20., position.y, 50 | position.z + (sin(time) + 10.) / 15.)) - 51 | radius; 52 | 53 | // This is different from the ground equation because the UV is only 54 | // between -1 and 1 we want more than 1/2pi of a wave per length of the 55 | // screen so we multiply the position by a factor of 10 inside the trig 56 | // functions. Since sin and cos oscillate between -1 and 1, that would be 57 | // the entire height of the screen so we divide by a factor of 10. 58 | float ground = position.y + sin(position.x * 10.) / 10. + 59 | cos(position.z * 10.) / 10. + 1.; 60 | 61 | // We want to return whichever one is closest to the ray, so we return the 62 | // minimum distance. 63 | return sphere; 64 | min(sphere, ground); 65 | } 66 | 67 | vec3 estimateNormal(vec3 p) { 68 | vec3 n = vec3(scene(vec3(p.x + smallNumber, p.yz)) - 69 | scene(vec3(p.x - smallNumber, p.yz)), 70 | scene(vec3(p.x, p.y + smallNumber, p.z)) - 71 | scene(vec3(p.x, p.y - smallNumber, p.z)), 72 | scene(vec3(p.xy, p.z + smallNumber)) - 73 | scene(vec3(p.xy, p.z - smallNumber))); 74 | // poke around the point to get the line perpandicular 75 | // to the surface at p, a point in space. 76 | return normalize(n); 77 | } 78 | 79 | vec4 lighting(vec3 pos, vec3 rd) { 80 | 81 | vec3 normal = estimateNormal(pos); 82 | 83 | // red light 84 | vec3 direction1 = normalize(vec3(-.9, 1, 0)); 85 | vec3 color1 = vec3(0.5, 1.3, 1.3); 86 | vec3 dif1 = color1 * orenNayarDiffuse(direction1, -rd, normal, 0.15, 1.0); 87 | vec3 spc1 = color1 * gaussianSpecular(direction1, -rd, normal, 0.15); 88 | 89 | // blue light 90 | vec3 direction2 = normalize(vec3(0.4, 0.5, -0.4)); 91 | vec3 color2 = vec3(1.3, 1.3, 0.9); 92 | vec3 dif2 = color2 * orenNayarDiffuse(direction2, -rd, normal, 0.15, 1.0); 93 | vec3 spc2 = color2 * gaussianSpecular(direction2, -rd, normal, 0.15); 94 | vec3 color = dif1 + spc1 + dif2 + spc2; 95 | color *= 0.5; 96 | color += white * 0.5; 97 | color *= getCam(normal.xy * 0.8); 98 | // color = getCam(normal.xy ); 99 | return vec4(color, 1.0); 100 | } 101 | 102 | vec4 trace(vec3 origin, vec3 direction) { 103 | 104 | float dist = 0.; 105 | float totalDistance = 0.; 106 | vec3 positionOnRay = origin; 107 | 108 | for (int i = 0; i < steps; i++) { 109 | 110 | dist = scene(positionOnRay); 111 | 112 | // Advance along the ray trajectory the amount that we know the ray 113 | // can travel without going through an object. 114 | positionOnRay += dist * direction; 115 | 116 | // Total distance is keeping track of how much the ray has traveled 117 | // thus far. 118 | totalDistance += dist; 119 | 120 | // If we hit an object or are close enough to an object, 121 | if (dist < smallNumber) { 122 | // return the lighting 123 | return lighting(positionOnRay, direction); 124 | } 125 | 126 | if (totalDistance > maxDist) { 127 | 128 | return vec4(black, 1.); // Background color. 129 | } 130 | } 131 | return vec4(black, 1.); // Background color. 132 | } 133 | 134 | vec3 lookAt(vec2 uv, vec3 camOrigin, vec3 camTarget) { 135 | // we get the z Axis the same way we got the direction vector before 136 | vec3 zAxis = normalize(camTarget - camOrigin); 137 | vec3 up = vec3(0, 1, 0); 138 | // cross product of two vectors produces a third vector that is 139 | // orthogonal to the first two (if you were to make a plane 140 | // with the first two vectors the third is perpendicular to that 141 | // plane. Which direction is determined by the 'right hand rule' 142 | // It is not communicative, so the order here matters. 143 | vec3 xAxis = normalize(cross(up, zAxis)); 144 | vec3 yAxis = normalize(cross(zAxis, xAxis)); 145 | // normalizing makes the vector of length one by dividing the 146 | // vector by the sum of squares (the norm). 147 | 148 | float fov = 2.; 149 | // scale each unit vector (aka vector of length one) by the ray origin 150 | // one for x one for y, there is no z vector so we just add it 151 | // then we finally scale by FOV 152 | vec3 dir = (normalize((uv.x * xAxis) + (uv.y * yAxis) + (zAxis * fov))); 153 | 154 | return dir; 155 | } 156 | 157 | void main() { 158 | 159 | vec2 pos = uv; 160 | pos.x *= resolution.x / resolution.y; 161 | 162 | vec3 camOrigin = vec3(0, 0, -4.0); 163 | // vec3 rayOrigin = vec3(pos + camOrigin.xy, camOrigin.z + 1.); 164 | vec3 target = vec3(-faceCenter, 0); 165 | vec3 dir = lookAt(pos, camOrigin, target); 166 | vec4 color = vec4(trace(camOrigin, dir)); 167 | if (length(color.rgb) < 0.1) { 168 | color.rgb = getCam(uv); 169 | // color.r = 1. 170 | } else { 171 | color.rgb += getCam(uv) * 0.1; 172 | } 173 | gl_FragColor = color; 174 | } -------------------------------------------------------------------------------- /docs/demos/rayblob.glsl: -------------------------------------------------------------------------------- 1 | 2 | // diffuse is the softer part of the light 3 | float orenNayarDiffuse(vec3 lightDirection, vec3 viewDirection, 4 | vec3 surfaceNormal, float roughness, float albedo) { 5 | 6 | float LdotV = dot(lightDirection, viewDirection); 7 | float NdotL = dot(lightDirection, surfaceNormal); 8 | float NdotV = dot(surfaceNormal, viewDirection); 9 | 10 | float s = LdotV - NdotL * NdotV; 11 | float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); 12 | 13 | float sigma2 = roughness * roughness; 14 | float A = 1.0 + sigma2 * (albedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33)); 15 | float B = 0.45 * sigma2 / (sigma2 + 0.09); 16 | 17 | return albedo * max(0.0, NdotL) * (A + B * s / t) / PI; 18 | } 19 | 20 | // specular means the shiny parts 21 | float gaussianSpecular(vec3 lightDirection, vec3 viewDirection, 22 | vec3 surfaceNormal, float shininess) { 23 | vec3 H = normalize(lightDirection + viewDirection); 24 | float theta = acos(dot(H, surfaceNormal)); 25 | float w = theta / shininess; 26 | return exp(-w * w); 27 | } 28 | 29 | // Define some constants 30 | const int steps = 128; // This is the maximum amount a ray can march. 31 | const float smallNumber = 0.001; 32 | const float maxDist = 10.; // This is the maximum distance a ray can travel. 33 | 34 | float scene(vec3 position) { 35 | // So this is different from the normal sphere equation in that I am 36 | // splitting the position into it's three different parts 37 | // and adding a 10th of a cos wave to the x position so it oscillates left 38 | // to right and a (positive) sin wave to the z position 39 | // so it will go back and forth. 40 | 41 | float radius = 1.9; 42 | 43 | vec2 eyeVector = leftEye - rightEye; 44 | float occularDistance = length(eyeVector); 45 | 46 | radius += noise(position * 1.7) * 0.58; 47 | radius *= occularDistance; 48 | 49 | float sphere = length(vec3(position.x + cos(time) / 20., position.y, 50 | position.z + (sin(time) + 10.) / 15.)) - 51 | radius; 52 | 53 | // This is different from the ground equation because the UV is only 54 | // between -1 and 1 we want more than 1/2pi of a wave per length of the 55 | // screen so we multiply the position by a factor of 10 inside the trig 56 | // functions. Since sin and cos oscillate between -1 and 1, that would be 57 | // the entire height of the screen so we divide by a factor of 10. 58 | float ground = position.y + sin(position.x * 10.) / 10. + 59 | cos(position.z * 10.) / 10. + 1.; 60 | 61 | // We want to return whichever one is closest to the ray, so we return the 62 | // minimum distance. 63 | return sphere; 64 | min(sphere, ground); 65 | } 66 | 67 | vec3 estimateNormal(vec3 p) { 68 | vec3 n = vec3(scene(vec3(p.x + smallNumber, p.yz)) - 69 | scene(vec3(p.x - smallNumber, p.yz)), 70 | scene(vec3(p.x, p.y + smallNumber, p.z)) - 71 | scene(vec3(p.x, p.y - smallNumber, p.z)), 72 | scene(vec3(p.xy, p.z + smallNumber)) - 73 | scene(vec3(p.xy, p.z - smallNumber))); 74 | // poke around the point to get the line perpandicular 75 | // to the surface at p, a point in space. 76 | return normalize(n); 77 | } 78 | 79 | vec4 lighting(vec3 pos, vec3 rd) { 80 | 81 | vec3 normal = estimateNormal(pos); 82 | 83 | // red light 84 | vec3 direction1 = normalize(vec3(-.9, 1, 0)); 85 | vec3 color1 = vec3(0.5, 1.3, 1.3); 86 | vec3 dif1 = color1 * orenNayarDiffuse(direction1, -rd, normal, 0.15, 1.0); 87 | vec3 spc1 = color1 * gaussianSpecular(direction1, -rd, normal, 0.15); 88 | 89 | // blue light 90 | vec3 direction2 = normalize(vec3(0.4, 0.5, -0.4)); 91 | vec3 color2 = vec3(1.3, 1.3, 0.9); 92 | vec3 dif2 = color2 * orenNayarDiffuse(direction2, -rd, normal, 0.15, 1.0); 93 | vec3 spc2 = color2 * gaussianSpecular(direction2, -rd, normal, 0.15); 94 | vec3 color = dif1 + spc1 + dif2 + spc2; 95 | color *= 0.5; 96 | color += white * 0.5; 97 | color *= getCam(normal.xy * 0.8); 98 | // color = getCam(normal.xy ); 99 | return vec4(color, 1.0); 100 | } 101 | 102 | vec4 trace(vec3 origin, vec3 direction) { 103 | 104 | float dist = 0.; 105 | float totalDistance = 0.; 106 | vec3 positionOnRay = origin; 107 | 108 | for (int i = 0; i < steps; i++) { 109 | 110 | dist = scene(positionOnRay); 111 | 112 | // Advance along the ray trajectory the amount that we know the ray 113 | // can travel without going through an object. 114 | positionOnRay += dist * direction; 115 | 116 | // Total distance is keeping track of how much the ray has traveled 117 | // thus far. 118 | totalDistance += dist; 119 | 120 | // If we hit an object or are close enough to an object, 121 | if (dist < smallNumber) { 122 | // return the lighting 123 | return lighting(positionOnRay, direction); 124 | } 125 | 126 | if (totalDistance > maxDist) { 127 | 128 | return vec4(black, 1.); // Background color. 129 | } 130 | } 131 | return vec4(black, 1.); // Background color. 132 | } 133 | 134 | vec3 lookAt(vec2 uv, vec3 camOrigin, vec3 camTarget) { 135 | // we get the z Axis the same way we got the direction vector before 136 | vec3 zAxis = normalize(camTarget - camOrigin); 137 | vec3 up = vec3(0, 1, 0); 138 | // cross product of two vectors produces a third vector that is 139 | // orthogonal to the first two (if you were to make a plane 140 | // with the first two vectors the third is perpendicular to that 141 | // plane. Which direction is determined by the 'right hand rule' 142 | // It is not communicative, so the order here matters. 143 | vec3 xAxis = normalize(cross(up, zAxis)); 144 | vec3 yAxis = normalize(cross(zAxis, xAxis)); 145 | // normalizing makes the vector of length one by dividing the 146 | // vector by the sum of squares (the norm). 147 | 148 | float fov = 2.; 149 | // scale each unit vector (aka vector of length one) by the ray origin 150 | // one for x one for y, there is no z vector so we just add it 151 | // then we finally scale by FOV 152 | vec3 dir = (normalize((uv.x * xAxis) + (uv.y * yAxis) + (zAxis * fov))); 153 | 154 | return dir; 155 | } 156 | 157 | void main() { 158 | 159 | vec2 pos = uv; 160 | pos.x *= resolution.x / resolution.y; 161 | 162 | vec3 camOrigin = vec3(0, 0, -4.0); 163 | // vec3 rayOrigin = vec3(pos + camOrigin.xy, camOrigin.z + 1.); 164 | vec3 target = vec3(-faceCenter, 0); 165 | vec3 dir = lookAt(pos, camOrigin, target); 166 | vec4 color = vec4(trace(camOrigin, dir)); 167 | if (length(color.rgb) < 0.1) { 168 | color.rgb = getCam(uv); 169 | // color.r = 1. 170 | } else { 171 | color.rgb += getCam(uv) * 0.1; 172 | } 173 | gl_FragColor = color; 174 | } -------------------------------------------------------------------------------- /docs/main.css: -------------------------------------------------------------------------------- 1 | video { 2 | /* height: 100vh; */ 3 | width: 20%; 4 | height: 20%; 5 | /* margin-top: 100vh; */ 6 | position: absolute; 7 | } 8 | 9 | #start { 10 | position: absolute; 11 | z-index: 10; 12 | width: 100px; 13 | height: 100px; 14 | } 15 | #tray { 16 | position: fixed; 17 | right: 3; 18 | bottom: 3; 19 | z-index: 9; 20 | display: flex; 21 | pointer-events: none; 22 | } 23 | .tray-left { 24 | left: 3; 25 | right: unset; 26 | } 27 | #tray button { 28 | margin: 3px; 29 | height: 50px; 30 | width: 50px; 31 | line-height: 24px; 32 | font-size: 30px; 33 | padding: 0px; 34 | pointer-events: all; 35 | color: rgba(255, 255, 255, 0.8); 36 | background-color: rgba(0, 0, 0, 0.2); 37 | border-radius: 100%; 38 | /* border: 1px rgba(255, 255, 255, 0.4) double; */ 39 | border: 4px rgba(255, 255, 255, 0.4) double; 40 | 41 | filter: drop-shadow(1px 1px 3px black); 42 | } 43 | button { 44 | /* font-family: Astloch; */ 45 | } 46 | svg { 47 | /* stroke: rgba(255, 255, 255, 0.8); */ 48 | /* stroke-width: 40px; */ 49 | /* fill: transparent; */ 50 | fill: rgba(255, 255, 255, 0.8); 51 | } 52 | line { 53 | stroke: rgba(255, 255, 255, 0.8); 54 | stroke-width: 25px; 55 | } 56 | .open svg, 57 | .open line { 58 | fill: rgba(0, 0, 0, 0.8); 59 | stroke: rgba(0, 0, 0, 0.8) !important; 60 | } 61 | #share { 62 | font-weight: bold; 63 | /* float: right; */ 64 | filter: drop-shadow(0px 0px 3px green); 65 | } 66 | button:active { 67 | animation: pulse 0.1s 1 ease-out; 68 | } 69 | button:focus { 70 | outline: 0; 71 | filter: drop-shadow(1px 1px 3px white); 72 | } 73 | 74 | @keyframes pulse { 75 | from { 76 | box-shadow: 0 0 0 0 rgba(255, 255, 255, 1), 0 0 0 0 rgba(51, 51, 51, 1); 77 | } 78 | to { 79 | box-shadow: 0 0 0 2.5px rgba(255, 255, 255, 1), 80 | 0 0 0 5px rgba(51, 51, 51, 0); 81 | } 82 | } 83 | 84 | div[class^="pulse-"] { 85 | background-color: grey; 86 | border-radius: 50%; 87 | color: black; 88 | cursor: pointer; 89 | display: inline-block; 90 | height: 100px; 91 | line-height: 100px; 92 | margin: 1em; 93 | padding: 1em; 94 | text-align: center; 95 | width: 100px; 96 | } 97 | .open { 98 | background-color: rgba(255, 255, 255, 0.9) !important; 99 | border: 8px rgba(0, 0, 0, 0.9) double !important; 100 | 101 | color: black !important; 102 | /* filter:invert(100%); */ 103 | } 104 | .dialog { 105 | color: black; 106 | border-radius: 3px; 107 | position: absolute; 108 | background-color: rgba(255, 255, 255, 0.7); 109 | width: 600px; 110 | max-width: 95vw; 111 | 112 | margin: auto; 113 | z-index: 10; 114 | margin: 0px; 115 | bottom: 60px; 116 | border: 4px double rgba(0, 0, 0, 0.2); 117 | filter: invert() drop-shadow(1px 1px 3px rgba(0, 0, 0, 0.5)); 118 | /* border-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAA2klEQVQ4T61V2xGDMAxrdulvF+nALMIvu9Azd8oJITs82q+WOpIsW6G9/vhZvvP6nj6txRfgxoMRR1XfQQGC4gDmgyMS/X9TqqDxG8BQzoRcrwJQn4LGYVXM9qBVR7gDrTx19jiSELOB3vUws2cHWnmoA+H2dQYWFOpHdjAYSHr7CuLMr5TqntvpnwmDm8NwpbgdFwDtpmxfB5AlSkmZ5LCnV0CRPl5LG1Ndq8fZ14TwPcCqMo8Pg9IWMs90M9xVeOqWUkLe6xT0bvYze3pMNZJP3ggp6NWpc/0P0OsfHVsAmCkAAAAASUVORK5CYII=") */ 119 | /* 7 / 7px / 0 round; */ 120 | /* border-width: 7px; */ 121 | /* border-style: solid; */ 122 | padding: 20px; 123 | } 124 | #share-box { 125 | /* height: 150px; */ 126 | width: 350px; 127 | left: 10px; 128 | 129 | bottom: 60px; 130 | } 131 | .dialog-cta { 132 | padding-top: 15px; 133 | display: flex; 134 | justify-content: space-between; 135 | } 136 | #share-box button { 137 | font-family: "Astloch", cursive; 138 | font-size: 1.5em; 139 | width: 90px; 140 | } 141 | #url-copy { 142 | height: 34px; 143 | /* margin-bottom:3px; */ 144 | padding-left: 15px; 145 | margin-left: 5px; 146 | /* font-family: "Astloch", cursive; */ 147 | font-weight: bold; 148 | filter: drop-shadow(0px 0px 2px green); 149 | 150 | font-size: 0.9em; 151 | } 152 | #info-box { 153 | top: 5px; 154 | right: 8px; 155 | overflow-y: scroll; 156 | overflow-x: hidden; 157 | } 158 | h1 { 159 | margin-top: 0px; 160 | margin-left: -5px; 161 | font-size: 2.2em; 162 | margin-bottom: 5px; 163 | font-family: "Astloch", cursive; 164 | filter: drop-shadow(1px 1px 2px green); 165 | } 166 | h3 { 167 | font-family: "Astloch", cursive; 168 | filter: drop-shadow(1px 1px 2px green); 169 | 170 | margin-top: 0px; 171 | margin-left: -5px; 172 | font-size: 1.5em; 173 | margin-bottom: 5px; 174 | } 175 | p { 176 | /* font-family: "Astloch", cursive; */ 177 | /* font-size: 40px; */ 178 | margin-top: 0px; 179 | } 180 | pre { 181 | margin-top: 0px; 182 | } 183 | .hidden { 184 | display: none; 185 | } 186 | canvas { 187 | position: absolute; 188 | z-index: 0; 189 | width: 100%; 190 | height: 100%; 191 | padding: 0; 192 | margin: 0; 193 | left: 0; 194 | top: 0; 195 | } 196 | #paint { 197 | display: none; 198 | bottom: 0 !important; 199 | top: unset; 200 | } 201 | #target { 202 | /* display: none; */ 203 | /* z-index: -4; */ 204 | image-rendering: pixelated; 205 | } 206 | body { 207 | width: 100vw; 208 | margin: 0; 209 | background-color: black; 210 | background: linear-gradient(-95deg, #270a0855, #12061655), 211 | radial-gradient(circle, rgba(75, 6, 36, 0.5) 0%, rgba(5, 31, 59, 0.5) 100%); 212 | font-family: monospace; 213 | } 214 | 215 | .CodeMirror { 216 | position: absolute; 217 | /* top: 20px; 218 | left: 20px; */ 219 | z-index: 5; 220 | /* width: calc(100% - 20px); 221 | /* height: calc(100% - 20px); */ 222 | height: 100%; 223 | background: transparent; 224 | width: calc(100% - 60px); 225 | } 226 | 227 | .console { 228 | bottom: 0px; 229 | position: absolute; 230 | padding: 20px; 231 | padding-bottom: 5px; 232 | width: calc(100% - 40px); 233 | z-index: 6; 234 | left: 0px; 235 | font-family: monospace; 236 | font-size: 14px; 237 | color: #aaa; 238 | } 239 | 240 | .log-error { 241 | color: #f00; 242 | } 243 | 244 | .styled-background { 245 | /* background-color: #f00 !important; */ 246 | background: rgba(0, 255, 255, 0.5) !important; 247 | mix-blend-mode: difference; 248 | } 249 | 250 | #editor-container { 251 | width: 100%; 252 | height: 100%; 253 | position: fixed; 254 | padding: 20px; 255 | } 256 | 257 | .CodeMirror { 258 | /* Set height, width, borders, and global font properties here */ 259 | font-family: monospace; 260 | /* height: 300px; */ 261 | color: black; 262 | direction: ltr; 263 | } 264 | 265 | .fade { 266 | opacity: 1; 267 | transition: opacity 0.5s linear; 268 | -moz-transition: opacity 0.5s linear; 269 | -webkit-transition: opacity 0.5s linear; 270 | } 271 | 272 | .faded .fade { 273 | transition: opacity 3s linear; 274 | -moz-transition: opacity 3s linear; 275 | -webkit-transition: opacity 3s linear; 276 | opacity: 0; 277 | } 278 | 279 | @media only screen and (max-width: 600px) { 280 | .CodeMirror { 281 | display: none; 282 | } 283 | #open-share { 284 | display: none; 285 | } 286 | } 287 | 288 | #loading { 289 | position: fixed; 290 | left: 50%; 291 | top: 20%; 292 | z-index: 11; 293 | font-size: 1.5em; 294 | background-color: rgba(5, 250, 59, 0.2); 295 | } 296 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const regl = require("regl")("#target", { pixelRatio: 0.75 }); 2 | const { setupWebcam } = require("./src/setup-facemesh.js"); 3 | let shaders = require("./src/pack.shader.js"); 4 | let handleActivity = require("./src/fade.js"); 5 | let buildReference = require("./src/reference.js"); 6 | let share = require("./src/share.js"); 7 | let fs = require("fs"); 8 | let loadingShader = fs.readFileSync(__dirname + "/src/loadingShader.glsl"); 9 | let prefix = fs.readFileSync(__dirname + "/src/prefix.glsl").toString(); 10 | let demos = fs.readdirSync(__dirname + "/demos"); 11 | let previousDemo = document.getElementById("prev"); 12 | let nextDemo = document.getElementById("next"); 13 | 14 | let demoIndex = demos.indexOf("starter.glsl"); 15 | 16 | let { paintFace } = require("./src/paint"); 17 | const Editor = require("./src/editor.js"); 18 | 19 | var editor = new Editor(); 20 | 21 | const serverAddr = "https://api.shaderbooth.com:3002/"; 22 | function loadShaderFromServer() { 23 | if (window.location.search.length > 2) { 24 | let id = window.location.search.slice(1); 25 | fetch(serverAddr + "static/" + id) 26 | .then(response => { 27 | if (response.status == 200) { 28 | return response.text(); 29 | } else { 30 | alert("I couldnt' find that sketch"); 31 | } 32 | }) 33 | .then(data => { 34 | let code = JSON.parse(data); 35 | console.log(code); 36 | editor.setValue(code); 37 | }); 38 | } 39 | } 40 | loadShaderFromServer(); 41 | let shareButton = document.getElementById("share"); 42 | shareButton.addEventListener("click", () => { 43 | fetch(serverAddr + "upload/", { 44 | method: "post", 45 | body: JSON.stringify(editor.getValue()) 46 | }) 47 | .then(function(response) { 48 | return response.json(); 49 | }) 50 | .then(function({ id }) { 51 | window.history.pushState({}, "Shaderbooth", "?" + id); 52 | document.getElementById("url-copy").value = "Shaderbooth.com/?" + id; 53 | loadShaderFromServer(); 54 | }); 55 | }); 56 | 57 | function replaceShader() { 58 | let file = demos[demoIndex]; 59 | fetch("./demos/" + file) 60 | .then(response => { 61 | return response.text(); 62 | }) 63 | .then(data => { 64 | editor.setValue(data); 65 | }); 66 | } 67 | replaceShader(); 68 | previousDemo.addEventListener("click", () => { 69 | demoIndex = (demoIndex + demos.length - 1) % demos.length; 70 | replaceShader(); 71 | }); 72 | nextDemo.addEventListener("click", () => { 73 | demoIndex = (demoIndex + 1) % demos.length; 74 | replaceShader(); 75 | }); 76 | 77 | shaders.fragment = shaders.fragment.replace( 78 | `#define GLSLIFY 1 79 | `, 80 | "" 81 | ); 82 | let knownGoodShader = shaders.fragment; 83 | 84 | let prefixLength = prefix.split(/\r\n|\r|\n/).length - 1; 85 | let widgets = []; 86 | let markers = []; 87 | function clearHints(errors) { 88 | editor.cm.operation(function() { 89 | for (var i = 0; i < widgets.length; ++i) { 90 | editor.cm.removeLineWidget(widgets[i]); 91 | } 92 | widgets.length = 0; 93 | 94 | for (var i = 0; i < markers.length; ++i) { 95 | markers[i].clear(); 96 | } 97 | markers.length = 0; 98 | }); 99 | } 100 | 101 | function displayError(line, offset, message, token) { 102 | line = line - prefixLength; 103 | var msg = document.createElement("div"); 104 | if (widgets.some(widget => widget["_line_number"] == line)) { 105 | return; 106 | } 107 | msg.appendChild( 108 | document.createTextNode( 109 | ("^" + message).padStart(offset + message.length + 1, "\xa0") 110 | ) 111 | ); 112 | msg.className = "lint-error fade"; 113 | let lineWidget = editor.cm.addLineWidget(line - 1, msg, { 114 | coverGutter: false, 115 | noHScroll: true 116 | }); 117 | lineWidget["_line_number"] = line; 118 | widgets.push(lineWidget); 119 | markers.push( 120 | editor.cm.markText( 121 | { line: line - 1, ch: offset }, 122 | { line: line - 1, ch: offset + token.length }, 123 | { className: "cm-custom-error", attributes: { alt: message } } 124 | ) 125 | ); 126 | } 127 | 128 | // this relies on a special forked regl. 129 | window.shader_error_hook = displayError; 130 | editor.setValue(shaders.fragment); 131 | 132 | editor.cm.on("change", c => { 133 | let newShader = editor.getValue(); 134 | shaders.fragment = newShader; 135 | clearHints(); 136 | }); 137 | const lastFrame = regl.texture(); 138 | 139 | let paintElement = document.getElementById("paint"); //.getContext("2d"); 140 | let faceDetectionTexture; 141 | 142 | let hasFace = false; 143 | 144 | let faceCenter = [0.5, 0.5]; 145 | 146 | function convertCoordinate([fx, fy], videoWidth, videoHeight) { 147 | x = fx / videoWidth; 148 | y = fy / videoHeight; 149 | x = 1 - x; 150 | y = 1 - y; 151 | x = 2 * x - 1.0; 152 | y = 2 * y - 1.0; 153 | let targetAspect = window.innerWidth / window.innerHeight; 154 | let videoAspect = videoWidth / videoHeight; 155 | let uvA_x = x / (targetAspect / videoAspect); 156 | let uvA_y = y; 157 | if (targetAspect < videoAspect) { 158 | uvA_x = x; 159 | uvA_y = y / (videoAspect / targetAspect); 160 | } 161 | 162 | return [uvA_x, uvA_y]; 163 | } 164 | setupWebcam({ 165 | regl, 166 | done: (webcam, { videoWidth, videoHeight, getKeyPoints }) => { 167 | faceDetectionTexture = regl.texture(paintElement); 168 | // faceDetectionTexture.resize(videoWidth, videoHeight); 169 | 170 | let drawTriangle = regl({ 171 | uniforms: { 172 | camTex: webcam, 173 | previousTex: lastFrame, 174 | maskTex: faceDetectionTexture, 175 | videoResolution: [videoWidth, videoHeight], 176 | time: ({ time }) => time % 10000, 177 | hasFace: () => hasFace, 178 | resolution: ({ viewportWidth, viewportHeight }) => [ 179 | viewportWidth, 180 | viewportHeight 181 | ], 182 | targetAspect: () => window.innerWidth / window.innerHeight, 183 | scaledVideoResolution: ({ viewportWidth: vW, viewportHeight: vH }) => { 184 | let i; 185 | i = 186 | vW / vH > videoWidth / videoHeight 187 | ? [videoWidth * (vH / videoHeight), vH] 188 | : [vW, videoHeight * (vW / videoWidth)]; 189 | return i; 190 | }, 191 | faceCenter: () => 192 | convertCoordinate(faceCenter, videoWidth, videoHeight), 193 | leftEye: () => 194 | convertCoordinate(window.leftEye, videoWidth, videoHeight), 195 | rightEye: () => 196 | convertCoordinate(window.rightEye, videoWidth, videoHeight) 197 | }, 198 | 199 | frag: () => (hasFace ? prefix + shaders.fragment : loadingShader), 200 | vert: () => shaders.vertex, 201 | attributes: { 202 | // Full screen triangle 203 | position: [ 204 | [-1, 4], 205 | [-1, -1], 206 | [4, -1] 207 | ] 208 | }, 209 | // Our triangle has 3 vertices 210 | count: 3 211 | }); 212 | 213 | regl.frame(function(context) { 214 | let keyPoints = getKeyPoints(); 215 | // regl.clear({ 216 | // color: [0, 0, 0, 1] 217 | // }); 218 | if (keyPoints) { 219 | hasFace = true; 220 | faceCenter = keyPoints.noseTip[0]; 221 | // console.log(faceCenter[0]); 222 | // console.log(keyPoints.midwayBetweenEyes); 223 | // debugger; 224 | ctx = paintFace(keyPoints); 225 | faceDetectionTexture.subimage(ctx); 226 | } 227 | try { 228 | drawTriangle(); 229 | } catch (e) { 230 | console.log(e); 231 | // debugger; 232 | // editor.flashCode(100, 200); 233 | shaders.fragment = knownGoodShader; 234 | 235 | return; 236 | } 237 | knownGoodShader = shaders.fragment; 238 | 239 | lastFrame({ 240 | copy: true 241 | }); 242 | }); 243 | } 244 | }); 245 | -------------------------------------------------------------------------------- /docs/codemirror.css: -------------------------------------------------------------------------------- 1 | 2 | /* PADDING */ 3 | 4 | .CodeMirror-lines { 5 | padding: 4px 0; /* Vertical padding around content */ 6 | } 7 | .CodeMirror pre { 8 | padding: 0 4px; /* Horizontal padding of content */ 9 | } 10 | 11 | .CodeMirror-scrollbar-filler, 12 | .CodeMirror-gutter-filler { 13 | background-color: white; /* The little square between H and V scrollbars */ 14 | } 15 | 16 | /* GUTTER */ 17 | 18 | .CodeMirror-gutters { 19 | border-right: 1px solid #ddd; 20 | background-color: #f7f7f7; 21 | white-space: nowrap; 22 | } 23 | .CodeMirror-linenumbers { 24 | } 25 | .CodeMirror-linenumber { 26 | padding: 0 3px 0 5px; 27 | min-width: 20px; 28 | text-align: right; 29 | color: #999; 30 | white-space: nowrap; 31 | } 32 | 33 | .CodeMirror-guttermarker { 34 | color: black; 35 | } 36 | .CodeMirror-guttermarker-subtle { 37 | color: #999; 38 | } 39 | 40 | /* CURSOR */ 41 | 42 | .CodeMirror-cursor { 43 | border-left: 1px solid black; 44 | border-right: none; 45 | width: 0; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .cm-fat-cursor .CodeMirror-cursor { 52 | width: auto; 53 | border: 0 !important; 54 | background: #7e7; 55 | } 56 | .cm-fat-cursor div.CodeMirror-cursors { 57 | z-index: 1; 58 | } 59 | .cm-fat-cursor-mark { 60 | background-color: rgba(20, 255, 20, 0.5); 61 | -webkit-animation: blink 1.06s steps(1) infinite; 62 | -moz-animation: blink 1.06s steps(1) infinite; 63 | animation: blink 1.06s steps(1) infinite; 64 | } 65 | .cm-animate-fat-cursor { 66 | width: auto; 67 | border: 0; 68 | -webkit-animation: blink 1.06s steps(1) infinite; 69 | -moz-animation: blink 1.06s steps(1) infinite; 70 | animation: blink 1.06s steps(1) infinite; 71 | background-color: #7e7; 72 | } 73 | @-moz-keyframes blink { 74 | 0% { 75 | } 76 | 50% { 77 | background-color: transparent; 78 | } 79 | 100% { 80 | } 81 | } 82 | @-webkit-keyframes blink { 83 | 0% { 84 | } 85 | 50% { 86 | background-color: transparent; 87 | } 88 | 100% { 89 | } 90 | } 91 | @keyframes blink { 92 | 0% { 93 | } 94 | 50% { 95 | background-color: transparent; 96 | } 97 | 100% { 98 | } 99 | } 100 | 101 | /* Can style cursor different in overwrite (non-insert) mode */ 102 | .CodeMirror-overwrite .CodeMirror-cursor { 103 | } 104 | 105 | .cm-tab { 106 | display: inline-block; 107 | text-decoration: inherit; 108 | } 109 | 110 | .CodeMirror-rulers { 111 | position: absolute; 112 | left: 0; 113 | right: 0; 114 | top: -50px; 115 | bottom: -20px; 116 | overflow: hidden; 117 | } 118 | .CodeMirror-ruler { 119 | border-left: 1px solid #ccc; 120 | top: 0; 121 | bottom: 0; 122 | position: absolute; 123 | } 124 | 125 | /* DEFAULT THEME */ 126 | 127 | .cm-s-default .cm-header { 128 | color: blue; 129 | } 130 | .cm-s-default .cm-quote { 131 | color: #090; 132 | } 133 | .cm-negative { 134 | color: #d44; 135 | } 136 | .cm-positive { 137 | color: #292; 138 | } 139 | .cm-header, 140 | .cm-strong { 141 | font-weight: bold; 142 | } 143 | .cm-em { 144 | font-style: italic; 145 | } 146 | .cm-link { 147 | text-decoration: underline; 148 | } 149 | .cm-strikethrough { 150 | text-decoration: line-through; 151 | } 152 | 153 | .cm-s-default .cm-keyword { 154 | color: #708; 155 | } 156 | .cm-s-default .cm-atom { 157 | color: #219; 158 | } 159 | .cm-s-default .cm-number { 160 | color: #164; 161 | } 162 | .cm-s-default .cm-def { 163 | color: #00f; 164 | } 165 | .cm-s-default .cm-variable, 166 | .cm-s-default .cm-punctuation, 167 | .cm-s-default .cm-property, 168 | .cm-s-default .cm-operator { 169 | } 170 | .cm-s-default .cm-variable-2 { 171 | color: #05a; 172 | } 173 | .cm-s-default .cm-variable-3, 174 | .cm-s-default .cm-type { 175 | color: #085; 176 | } 177 | .cm-s-default .cm-comment { 178 | color: #a50; 179 | } 180 | .cm-s-default .cm-string { 181 | color: #a11; 182 | } 183 | .cm-s-default .cm-string-2 { 184 | color: #f50; 185 | } 186 | .cm-s-default .cm-meta { 187 | color: #555; 188 | } 189 | .cm-s-default .cm-qualifier { 190 | color: #555; 191 | } 192 | .cm-s-default .cm-builtin { 193 | color: #30a; 194 | } 195 | .cm-s-default .cm-bracket { 196 | color: #997; 197 | } 198 | .cm-s-default .cm-tag { 199 | color: #170; 200 | } 201 | .cm-s-default .cm-attribute { 202 | color: #00c; 203 | } 204 | .cm-s-default .cm-hr { 205 | color: #999; 206 | } 207 | .cm-s-default .cm-link { 208 | color: #00c; 209 | } 210 | 211 | .cm-s-default .cm-error { 212 | color: #f00; 213 | } 214 | .cm-invalidchar { 215 | color: #f00; 216 | } 217 | 218 | .CodeMirror-composing { 219 | border-bottom: 2px solid; 220 | } 221 | 222 | /* Default styles for common addons */ 223 | 224 | div.CodeMirror span.CodeMirror-matchingbracket { 225 | color: #0b0; 226 | } 227 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 228 | color: #a22; 229 | } 230 | .CodeMirror-matchingtag { 231 | background: rgba(255, 150, 0, 0.3); 232 | } 233 | .CodeMirror-activeline-background { 234 | background: #e8f2ff; 235 | } 236 | 237 | /* STOP */ 238 | 239 | /* The rest of this file contains styles related to the mechanics of 240 | the editor. You probably shouldn't touch them. */ 241 | 242 | .CodeMirror { 243 | position: relative; 244 | overflow: hidden; 245 | background: white; 246 | } 247 | 248 | .CodeMirror-scroll { 249 | overflow: scroll !important; /* Things will break if this is overridden */ 250 | /* 30px is the magic margin used to hide the element's real scrollbars */ 251 | /* See overflow: hidden in .CodeMirror */ 252 | margin-bottom: -30px; 253 | margin-right: -30px; 254 | padding-bottom: 30px; 255 | height: 100%; 256 | outline: none; /* Prevent dragging from highlighting the element */ 257 | position: relative; 258 | } 259 | .CodeMirror-sizer { 260 | position: relative; 261 | border-right: 30px solid transparent; 262 | } 263 | 264 | /* The fake, visible scrollbars. Used to force redraw during scrolling 265 | before actual scrolling happens, thus preventing shaking and 266 | flickering artifacts. */ 267 | .CodeMirror-vscrollbar, 268 | .CodeMirror-hscrollbar, 269 | .CodeMirror-scrollbar-filler, 270 | .CodeMirror-gutter-filler { 271 | position: absolute; 272 | z-index: 6; 273 | visibility: hidden; 274 | display: none; 275 | } 276 | .CodeMirror-vscrollbar { 277 | right: 0; 278 | top: 0; 279 | overflow-x: hidden; 280 | overflow-y: scroll; 281 | } 282 | .CodeMirror-hscrollbar { 283 | bottom: 0; 284 | left: 0; 285 | overflow-y: hidden; 286 | overflow-x: scroll; 287 | } 288 | .CodeMirror-scrollbar-filler { 289 | right: 0; 290 | bottom: 0; 291 | } 292 | .CodeMirror-gutter-filler { 293 | left: 0; 294 | bottom: 0; 295 | } 296 | 297 | .CodeMirror-gutters { 298 | position: absolute; 299 | left: 0; 300 | top: 0; 301 | min-height: 100%; 302 | z-index: 3; 303 | } 304 | .CodeMirror-gutter { 305 | white-space: normal; 306 | height: 100%; 307 | display: inline-block; 308 | vertical-align: top; 309 | margin-bottom: -30px; 310 | } 311 | .CodeMirror-gutter-wrapper { 312 | position: absolute; 313 | z-index: 4; 314 | background: none !important; 315 | border: none !important; 316 | } 317 | .CodeMirror-gutter-background { 318 | position: absolute; 319 | top: 0; 320 | bottom: 0; 321 | z-index: 4; 322 | } 323 | .CodeMirror-gutter-elt { 324 | position: absolute; 325 | cursor: default; 326 | z-index: 4; 327 | } 328 | .CodeMirror-gutter-wrapper ::selection { 329 | background-color: transparent; 330 | } 331 | .CodeMirror-gutter-wrapper ::-moz-selection { 332 | background-color: transparent; 333 | } 334 | 335 | .CodeMirror-lines { 336 | cursor: text; 337 | min-height: 1px; /* prevents collapsing before first draw */ 338 | } 339 | .CodeMirror pre { 340 | /* Reset some styles that the rest of the page might have set */ 341 | -moz-border-radius: 0; 342 | -webkit-border-radius: 0; 343 | border-radius: 0; 344 | border-width: 0; 345 | background: transparent; 346 | font-family: inherit; 347 | font-size: inherit; 348 | margin: 0; 349 | white-space: pre; 350 | word-wrap: normal; 351 | line-height: inherit; 352 | color: inherit; 353 | z-index: 2; 354 | position: relative; 355 | overflow: visible; 356 | -webkit-tap-highlight-color: transparent; 357 | -webkit-font-variant-ligatures: contextual; 358 | font-variant-ligatures: contextual; 359 | } 360 | .CodeMirror-wrap pre { 361 | word-wrap: break-word; 362 | white-space: pre-wrap; 363 | word-break: normal; 364 | } 365 | 366 | .CodeMirror-linebackground { 367 | position: absolute; 368 | left: 0; 369 | right: 0; 370 | top: 0; 371 | bottom: 0; 372 | z-index: 0; 373 | } 374 | 375 | .CodeMirror-linewidget { 376 | position: relative; 377 | z-index: 2; 378 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 379 | } 380 | 381 | .CodeMirror-widget { 382 | } 383 | 384 | .CodeMirror-rtl pre { 385 | direction: rtl; 386 | } 387 | 388 | .CodeMirror-code { 389 | outline: none; 390 | } 391 | 392 | /* Force content-box sizing for the elements where we expect it */ 393 | .CodeMirror-scroll, 394 | .CodeMirror-sizer, 395 | .CodeMirror-gutter, 396 | .CodeMirror-gutters, 397 | .CodeMirror-linenumber { 398 | -moz-box-sizing: content-box; 399 | box-sizing: content-box; 400 | } 401 | 402 | .CodeMirror-measure { 403 | position: absolute; 404 | width: 100%; 405 | height: 0; 406 | overflow: hidden; 407 | visibility: hidden; 408 | } 409 | 410 | .CodeMirror-cursor { 411 | position: absolute; 412 | pointer-events: none; 413 | } 414 | .CodeMirror-measure pre { 415 | position: static; 416 | } 417 | 418 | div.CodeMirror-cursors { 419 | visibility: hidden; 420 | position: relative; 421 | z-index: 3; 422 | } 423 | div.CodeMirror-dragcursors { 424 | visibility: visible; 425 | } 426 | 427 | .CodeMirror-focused div.CodeMirror-cursors { 428 | visibility: visible; 429 | } 430 | 431 | .CodeMirror-selected { 432 | background: #d9d9d9; 433 | } 434 | .CodeMirror-focused .CodeMirror-selected { 435 | background: #d7d4f0; 436 | } 437 | .CodeMirror-crosshair { 438 | cursor: crosshair; 439 | } 440 | .CodeMirror-line::selection, 441 | .CodeMirror-line > span::selection, 442 | .CodeMirror-line > span > span::selection { 443 | background: #d7d4f0; 444 | } 445 | .CodeMirror-line::-moz-selection, 446 | .CodeMirror-line > span::-moz-selection, 447 | .CodeMirror-line > span > span::-moz-selection { 448 | background: #d7d4f0; 449 | } 450 | 451 | .cm-searching { 452 | background-color: #ffa; 453 | background-color: rgba(255, 255, 0, 0.4); 454 | } 455 | 456 | /* Used to force a border model for a node */ 457 | .cm-force-border { 458 | padding-right: 0.1px; 459 | } 460 | 461 | @media print { 462 | /* Hide the cursor when printing */ 463 | .CodeMirror div.CodeMirror-cursors { 464 | visibility: hidden; 465 | } 466 | } 467 | 468 | /* See issue #2901 */ 469 | .cm-tab-wrap-hack:after { 470 | content: ""; 471 | } 472 | 473 | /* Help users use markselection to safely style text background */ 474 | span.CodeMirror-selectedtext { 475 | background: none; 476 | } 477 | -------------------------------------------------------------------------------- /src/prefix.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | varying vec2 uv; 3 | uniform float time; 4 | uniform float targetAspect; 5 | uniform vec2 resolution; 6 | uniform vec2 videoResolution; 7 | uniform vec2 scaledVideoResolution; 8 | uniform vec2 faceCenter; 9 | uniform vec2 leftEye; 10 | uniform vec2 rightEye; 11 | uniform sampler2D camTex; 12 | uniform sampler2D maskTex; 13 | uniform sampler2D previousTex; 14 | // SPACER 15 | vec2 pixel = 1.0 / resolution; 16 | 17 | #define PI 3.1415926538 18 | #define TAU 6.283185307 19 | 20 | // SPACER 21 | 22 | vec3 getCam(vec2 pos) { 23 | float videoAspect = videoResolution.x / videoResolution.y; 24 | 25 | vec2 uvA = vec2(pos.x * targetAspect / videoAspect, pos.y); 26 | if (targetAspect < videoAspect) { 27 | uvA = vec2(pos.x, pos.y * videoAspect / targetAspect); 28 | } 29 | vec2 webcamCoord = (uvA) / 2.0 + vec2(0.5); 30 | vec2 flipwcord = vec2(1.) - webcamCoord; 31 | 32 | float howFar = 1.0; 33 | if (uvA.x < -1.0 || uvA.x > 1.0 || uvA.y < -1.0 || uvA.y > 1.0) { 34 | howFar = max(abs(uvA.x), abs(uvA.y)) - 1.0; 35 | howFar = 2.0 + float(int(howFar * 5.)); 36 | vec2 towardsCenter = vec2(0., 0.0); 37 | if (uvA.x < -1.) { 38 | towardsCenter = vec2(-1.0, 0.0); 39 | } else if (uvA.x > 1.) { 40 | towardsCenter = vec2(1.0, 0.0); 41 | } else if (uvA.y > 1.) { 42 | towardsCenter = vec2(0.0, 1.0); 43 | } else if (uvA.y < -1.) { 44 | towardsCenter = vec2(0.0, -1.0); 45 | } 46 | flipwcord += towardsCenter * (1. / 8.) * howFar; 47 | float blurRadius = 4.0 + howFar; 48 | 49 | float blurAngle = sin((flipwcord.x + flipwcord.y) * 555.534) * 3.14 * 2.; 50 | vec2 blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 51 | vec3 color = texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 52 | 53 | blurAngle += 2.0; 54 | blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 55 | color += texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 56 | 57 | blurAngle += 2.0; 58 | blurOffset = vec2(cos(blurAngle), sin(blurAngle)) * pixel * blurRadius; 59 | color += texture2D(camTex, flipwcord + blurOffset).rgb / 3.; 60 | 61 | return color; 62 | } 63 | 64 | return texture2D(camTex, flipwcord).rgb; 65 | } 66 | 67 | vec3 getPrevious(vec2 pos) { 68 | vec2 backCoord = (pos / 2.0) + vec2(0.5); 69 | return texture2D(previousTex, backCoord).rgb; 70 | } 71 | // SPACER 72 | 73 | // Access Facial Feature Masks 74 | 75 | float getFace(vec2 pos) { 76 | float videoAspect = videoResolution.x / videoResolution.y; 77 | vec2 uvA = vec2(pos.x * targetAspect / videoAspect, pos.y); 78 | if ((targetAspect) < (videoResolution.x / videoResolution.y)) { 79 | uvA = vec2(pos.x, pos.y * videoAspect / targetAspect); 80 | } 81 | vec2 webcamCoord = (uvA) / 2.0 + vec2(0.5); 82 | vec2 flipwcord = vec2(1.) - webcamCoord; 83 | return texture2D(maskTex, flipwcord).b; 84 | } 85 | 86 | float getEye(vec2 pos) { 87 | float videoAspect = videoResolution.x / videoResolution.y; 88 | vec2 uvA = vec2(pos.x * targetAspect / videoAspect, pos.y); 89 | if ((targetAspect) < (videoResolution.x / videoResolution.y)) { 90 | uvA = vec2(pos.x, pos.y * videoAspect / targetAspect); 91 | } 92 | vec2 webcamCoord = (uvA) / 2.0 + vec2(0.5); 93 | vec2 flipwcord = vec2(1.) - webcamCoord; 94 | return texture2D(maskTex, flipwcord).g; 95 | } 96 | 97 | float getMouth(vec2 pos) { 98 | float videoAspect = videoResolution.x / videoResolution.y; 99 | vec2 uvA = vec2(pos.x * targetAspect / videoAspect, pos.y); 100 | if ((targetAspect) < (videoResolution.x / videoResolution.y)) { 101 | uvA = vec2(pos.x, pos.y * videoAspect / targetAspect); 102 | } 103 | vec2 webcamCoord = (uvA) / 2.0 + vec2(0.5); 104 | vec2 flipwcord = vec2(1.) - webcamCoord; 105 | return texture2D(maskTex, flipwcord).r; 106 | } 107 | 108 | // SPACER 109 | vec2 rotate(vec2 v, float a) { 110 | float s = sin(a); 111 | float c = cos(a); 112 | mat2 m = mat2(c, -s, s, c); 113 | return m * v; 114 | } 115 | // SPACER 116 | 117 | float map(float value, float min1, float max1, float min2, float max2) { 118 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 119 | } 120 | 121 | // SPACER 122 | // Color Shorthands 123 | 124 | vec3 black = vec3(0.0); 125 | vec3 white = vec3(1.0); 126 | vec3 red = vec3(0.86, 0.22, 0.27); 127 | vec3 orange = vec3(0.92, 0.49, 0.07); 128 | vec3 yellow = vec3(0.91, 0.89, 0.26); 129 | vec3 green = vec3(0.0, 0.71, 0.31); 130 | vec3 blue = vec3(0.05, 0.35, 0.65); 131 | vec3 purple = vec3(0.38, 0.09, 0.64); 132 | vec3 pink = vec3(.9, 0.758, 0.798); 133 | vec3 lime = vec3(0.361, 0.969, 0.282); 134 | vec3 teal = vec3(0.396, 0.878, 0.878); 135 | vec3 magenta = vec3(1.0, 0.189, 0.745); 136 | vec3 brown = vec3(0.96, 0.474, 0.227); 137 | // SPACER 138 | float rand(float n) { return fract(sin(n) * 43758.5453123); } 139 | 140 | // 1D, 2D, 3D Simplex Noise 141 | float noise(float p) { 142 | float fl = floor(p); 143 | float fc = fract(p); 144 | return mix(rand(fl), rand(fl + 1.0), fc); 145 | } 146 | 147 | // INTERNAL 148 | vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 149 | // INTERNAL 150 | vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 151 | // INTERNAL 152 | vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); } 153 | 154 | float noise(vec2 v) { 155 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 156 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 157 | -0.577350269189626, // -1.0 + 2.0 * C.x 158 | 0.024390243902439); // 1.0 / 41.0 159 | // First corner 160 | vec2 i = floor(v + dot(v, C.yy)); 161 | vec2 x0 = v - i + dot(i, C.xx); 162 | 163 | // Other corners 164 | vec2 i1; 165 | // i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 166 | // i1.y = 1.0 - i1.x; 167 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 168 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 169 | // x1 = x0 - i1 + 1.0 * C.xx ; 170 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 171 | vec4 x12 = x0.xyxy + C.xxzz; 172 | x12.xy -= i1; 173 | 174 | // Permutations 175 | i = mod289(i); // Avoid truncation effects in permutation 176 | vec3 p = 177 | permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0)); 178 | 179 | vec3 m = max( 180 | 0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); 181 | m = m * m; 182 | m = m * m; 183 | 184 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 185 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 186 | 187 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 188 | vec3 h = abs(x) - 0.5; 189 | vec3 ox = floor(x + 0.5); 190 | vec3 a0 = x - ox; 191 | 192 | // Normalise gradients implicitly by scaling m 193 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 194 | m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); 195 | 196 | // Compute final noise value at P 197 | vec3 g; 198 | g.x = a0.x * x0.x + h.x * x0.y; 199 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 200 | return 130.0 * dot(m, g); 201 | } 202 | 203 | // INTERNAL 204 | vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 205 | // INTERNAL 206 | vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); } 207 | // INTERNAL 208 | vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } 209 | 210 | float noise(vec3 v) { 211 | const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); 212 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 213 | 214 | // First corner 215 | vec3 i = floor(v + dot(v, C.yyy)); 216 | vec3 x0 = v - i + dot(i, C.xxx); 217 | 218 | // Other corners 219 | vec3 g = step(x0.yzx, x0.xyz); 220 | vec3 l = 1.0 - g; 221 | vec3 i1 = min(g.xyz, l.zxy); 222 | vec3 i2 = max(g.xyz, l.zxy); 223 | 224 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 225 | // x1 = x0 - i1 + 1.0 * C.xxx; 226 | // x2 = x0 - i2 + 2.0 * C.xxx; 227 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 228 | vec3 x1 = x0 - i1 + C.xxx; 229 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 230 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 231 | 232 | // Permutations 233 | i = mod289(i); 234 | vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + 235 | vec4(0.0, i1.y, i2.y, 1.0)) + 236 | i.x + vec4(0.0, i1.x, i2.x, 1.0)); 237 | 238 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 239 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 240 | float n_ = 0.142857142857; // 1.0/7.0 241 | vec3 ns = n_ * D.wyz - D.xzx; 242 | 243 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 244 | 245 | vec4 x_ = floor(j * ns.z); 246 | vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) 247 | 248 | vec4 x = x_ * ns.x + ns.yyyy; 249 | vec4 y = y_ * ns.x + ns.yyyy; 250 | vec4 h = 1.0 - abs(x) - abs(y); 251 | 252 | vec4 b0 = vec4(x.xy, y.xy); 253 | vec4 b1 = vec4(x.zw, y.zw); 254 | 255 | // vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 256 | // vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 257 | vec4 s0 = floor(b0) * 2.0 + 1.0; 258 | vec4 s1 = floor(b1) * 2.0 + 1.0; 259 | vec4 sh = -step(h, vec4(0.0)); 260 | 261 | vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; 262 | vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; 263 | 264 | vec3 p0 = vec3(a0.xy, h.x); 265 | vec3 p1 = vec3(a0.zw, h.y); 266 | vec3 p2 = vec3(a1.xy, h.z); 267 | vec3 p3 = vec3(a1.zw, h.w); 268 | 269 | // Normalise gradients 270 | vec4 norm = 271 | taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); 272 | p0 *= norm.x; 273 | p1 *= norm.y; 274 | p2 *= norm.z; 275 | p3 *= norm.w; 276 | 277 | // Mix final noise value 278 | vec4 m = 279 | max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); 280 | m = m * m; 281 | return 42.0 * 282 | dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); 283 | } 284 | 285 | // Fractal Brownian Noise 286 | float fbn(float x, const in int iterations) { 287 | float v = 0.0; 288 | float a = 0.5; 289 | float shift = float(100); 290 | for (int i = 0; i < 32; ++i) { 291 | if (i < iterations) { 292 | v += a * noise(x); 293 | x = x * 2.0 + shift; 294 | a *= 0.5; 295 | } 296 | } 297 | return v; 298 | } 299 | 300 | float fbn(vec2 x, const in int iterations) { 301 | float v = 0.0; 302 | float a = 0.5; 303 | vec2 shift = vec2(100); 304 | // Rotate to reduce axial bias 305 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 306 | for (int i = 0; i < 32; ++i) { 307 | if (i < iterations) { 308 | v += a * noise(x); 309 | x = rot * x * 2.0 + shift; 310 | a *= 0.5; 311 | } 312 | } 313 | return v; 314 | } 315 | 316 | float fbn(vec3 x, const in int iterations) { 317 | float v = 0.0; 318 | float a = 0.5; 319 | vec3 shift = vec3(100); 320 | for (int i = 0; i < 32; ++i) { 321 | if (i < iterations) { 322 | v += a * noise(x); 323 | x = x * 2.0 + shift; 324 | a *= 0.5; 325 | } 326 | } 327 | return v; 328 | } 329 | // SPACER 330 | // INTERNAL 331 | float hue2rgb(float f1, float f2, float hue) { 332 | // http://www.chilliant.com/rgb2hsv.html 333 | 334 | if (hue < 0.0) 335 | hue += 1.0; 336 | else if (hue > 1.0) 337 | hue -= 1.0; 338 | float res; 339 | if ((6.0 * hue) < 1.0) 340 | res = f1 + (f2 - f1) * 6.0 * hue; 341 | else if ((2.0 * hue) < 1.0) 342 | res = f2; 343 | else if ((3.0 * hue) < 2.0) 344 | res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; 345 | else 346 | res = f1; 347 | return res; 348 | } 349 | 350 | // Color Space Conversion 351 | 352 | vec3 hsl2rgb(vec3 hsl) { 353 | vec3 rgb; 354 | 355 | if (hsl.y == 0.0) { 356 | rgb = vec3(hsl.z); // Luminance 357 | } else { 358 | float f2; 359 | 360 | if (hsl.z < 0.5) 361 | f2 = hsl.z * (1.0 + hsl.y); 362 | else 363 | f2 = hsl.z + hsl.y - hsl.y * hsl.z; 364 | 365 | float f1 = 2.0 * hsl.z - f2; 366 | 367 | rgb.r = hue2rgb(f1, f2, hsl.x + (1.0 / 3.0)); 368 | rgb.g = hue2rgb(f1, f2, hsl.x); 369 | rgb.b = hue2rgb(f1, f2, hsl.x - (1.0 / 3.0)); 370 | } 371 | return rgb; 372 | } 373 | 374 | vec3 hsl2rgb(float h, float s, float l) { return hsl2rgb(vec3(h, s, l)); } 375 | 376 | // INTERNAL 377 | vec3 RGBtoHCV(vec3 rgb) { 378 | // Based on work by Sam Hocevar and Emil Persson 379 | vec4 p = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0 / 3.0) 380 | : vec4(rgb.gb, 0.0, -1.0 / 3.0); 381 | vec4 q = (rgb.r < p.x) ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx); 382 | float c = q.x - min(q.w, q.y); 383 | float h = abs((q.w - q.y) / (6.0 * c + 1e-10) + q.z); 384 | return vec3(h, c, q.x); 385 | } 386 | 387 | vec3 rgb2hsl(vec3 rgb) { 388 | vec3 hcv = RGBtoHCV(rgb); 389 | float l = hcv.z - hcv.y * 0.5; 390 | float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + 1e-10); 391 | return vec3(hcv.x, s, l); 392 | } 393 | 394 | vec3 rgb2hsl(float r, float g, float b) { return rgb2hsl(vec3(r, g, b)); } 395 | 396 | float luma(vec3 color) { return dot(color, vec3(0.299, 0.587, 0.114)); } 397 | -------------------------------------------------------------------------------- /src/triangulation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2020 Google LLC. All Rights Reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * ============================================================================= 16 | */ 17 | 18 | const TRIANGULATION = [ 19 | 127, 34, 139, 11, 0, 37, 232, 231, 120, 72, 37, 39, 128, 121, 47, 232, 121, 20 | 128, 104, 69, 67, 175, 171, 148, 157, 154, 155, 118, 50, 101, 73, 39, 40, 9, 21 | 151, 108, 48, 115, 131, 194, 204, 211, 74, 40, 185, 80, 42, 183, 40, 92, 22 | 186, 230, 229, 118, 202, 212, 214, 83, 18, 17, 76, 61, 146, 160, 29, 30, 56, 23 | 157, 173, 106, 204, 194, 135, 214, 192, 203, 165, 98, 21, 71, 68, 51, 45, 4, 24 | 144, 24, 23, 77, 146, 91, 205, 50, 187, 201, 200, 18, 91, 106, 182, 90, 91, 25 | 181, 85, 84, 17, 206, 203, 36, 148, 171, 140, 92, 40, 39, 193, 189, 244, 26 | 159, 158, 28, 247, 246, 161, 236, 3, 196, 54, 68, 104, 193, 168, 8, 117, 27 | 228, 31, 189, 193, 55, 98, 97, 99, 126, 47, 100, 166, 79, 218, 155, 154, 26, 28 | 209, 49, 131, 135, 136, 150, 47, 126, 217, 223, 52, 53, 45, 51, 134, 211, 29 | 170, 140, 67, 69, 108, 43, 106, 91, 230, 119, 120, 226, 130, 247, 63, 53, 30 | 52, 238, 20, 242, 46, 70, 156, 78, 62, 96, 46, 53, 63, 143, 34, 227, 173, 31 | 155, 133, 123, 117, 111, 44, 125, 19, 236, 134, 51, 216, 206, 205, 154, 153, 32 | 22, 39, 37, 167, 200, 201, 208, 36, 142, 100, 57, 212, 202, 20, 60, 99, 28, 33 | 158, 157, 35, 226, 113, 160, 159, 27, 204, 202, 210, 113, 225, 46, 43, 202, 34 | 204, 62, 76, 77, 137, 123, 116, 41, 38, 72, 203, 129, 142, 64, 98, 240, 49, 35 | 102, 64, 41, 73, 74, 212, 216, 207, 42, 74, 184, 169, 170, 211, 170, 149, 36 | 176, 105, 66, 69, 122, 6, 168, 123, 147, 187, 96, 77, 90, 65, 55, 107, 89, 37 | 90, 180, 101, 100, 120, 63, 105, 104, 93, 137, 227, 15, 86, 85, 129, 102, 38 | 49, 14, 87, 86, 55, 8, 9, 100, 47, 121, 145, 23, 22, 88, 89, 179, 6, 122, 39 | 196, 88, 95, 96, 138, 172, 136, 215, 58, 172, 115, 48, 219, 42, 80, 81, 195, 40 | 3, 51, 43, 146, 61, 171, 175, 199, 81, 82, 38, 53, 46, 225, 144, 163, 110, 41 | 246, 33, 7, 52, 65, 66, 229, 228, 117, 34, 127, 234, 107, 108, 69, 109, 108, 42 | 151, 48, 64, 235, 62, 78, 191, 129, 209, 126, 111, 35, 143, 163, 161, 246, 43 | 117, 123, 50, 222, 65, 52, 19, 125, 141, 221, 55, 65, 3, 195, 197, 25, 7, 44 | 33, 220, 237, 44, 70, 71, 139, 122, 193, 245, 247, 130, 33, 71, 21, 162, 45 | 153, 158, 159, 170, 169, 150, 188, 174, 196, 216, 186, 92, 144, 160, 161, 2, 46 | 97, 167, 141, 125, 241, 164, 167, 37, 72, 38, 12, 145, 159, 160, 38, 82, 13, 47 | 63, 68, 71, 226, 35, 111, 158, 153, 154, 101, 50, 205, 206, 92, 165, 209, 48 | 198, 217, 165, 167, 97, 220, 115, 218, 133, 112, 243, 239, 238, 241, 214, 49 | 135, 169, 190, 173, 133, 171, 208, 32, 125, 44, 237, 86, 87, 178, 85, 86, 50 | 179, 84, 85, 180, 83, 84, 181, 201, 83, 182, 137, 93, 132, 76, 62, 183, 61, 51 | 76, 184, 57, 61, 185, 212, 57, 186, 214, 207, 187, 34, 143, 156, 79, 239, 52 | 237, 123, 137, 177, 44, 1, 4, 201, 194, 32, 64, 102, 129, 213, 215, 138, 59, 53 | 166, 219, 242, 99, 97, 2, 94, 141, 75, 59, 235, 24, 110, 228, 25, 130, 226, 54 | 23, 24, 229, 22, 23, 230, 26, 22, 231, 112, 26, 232, 189, 190, 243, 221, 56, 55 | 190, 28, 56, 221, 27, 28, 222, 29, 27, 223, 30, 29, 224, 247, 30, 225, 238, 56 | 79, 20, 166, 59, 75, 60, 75, 240, 147, 177, 215, 20, 79, 166, 187, 147, 213, 57 | 112, 233, 244, 233, 128, 245, 128, 114, 188, 114, 217, 174, 131, 115, 220, 58 | 217, 198, 236, 198, 131, 134, 177, 132, 58, 143, 35, 124, 110, 163, 7, 228, 59 | 110, 25, 356, 389, 368, 11, 302, 267, 452, 350, 349, 302, 303, 269, 357, 60 | 343, 277, 452, 453, 357, 333, 332, 297, 175, 152, 377, 384, 398, 382, 347, 61 | 348, 330, 303, 304, 270, 9, 336, 337, 278, 279, 360, 418, 262, 431, 304, 62 | 408, 409, 310, 415, 407, 270, 409, 410, 450, 348, 347, 422, 430, 434, 313, 63 | 314, 17, 306, 307, 375, 387, 388, 260, 286, 414, 398, 335, 406, 418, 364, 64 | 367, 416, 423, 358, 327, 251, 284, 298, 281, 5, 4, 373, 374, 253, 307, 320, 65 | 321, 425, 427, 411, 421, 313, 18, 321, 405, 406, 320, 404, 405, 315, 16, 17, 66 | 426, 425, 266, 377, 400, 369, 322, 391, 269, 417, 465, 464, 386, 257, 258, 67 | 466, 260, 388, 456, 399, 419, 284, 332, 333, 417, 285, 8, 346, 340, 261, 68 | 413, 441, 285, 327, 460, 328, 355, 371, 329, 392, 439, 438, 382, 341, 256, 69 | 429, 420, 360, 364, 394, 379, 277, 343, 437, 443, 444, 283, 275, 440, 363, 70 | 431, 262, 369, 297, 338, 337, 273, 375, 321, 450, 451, 349, 446, 342, 467, 71 | 293, 334, 282, 458, 461, 462, 276, 353, 383, 308, 324, 325, 276, 300, 293, 72 | 372, 345, 447, 382, 398, 362, 352, 345, 340, 274, 1, 19, 456, 248, 281, 436, 73 | 427, 425, 381, 256, 252, 269, 391, 393, 200, 199, 428, 266, 330, 329, 287, 74 | 273, 422, 250, 462, 328, 258, 286, 384, 265, 353, 342, 387, 259, 257, 424, 75 | 431, 430, 342, 353, 276, 273, 335, 424, 292, 325, 307, 366, 447, 345, 271, 76 | 303, 302, 423, 266, 371, 294, 455, 460, 279, 278, 294, 271, 272, 304, 432, 77 | 434, 427, 272, 407, 408, 394, 430, 431, 395, 369, 400, 334, 333, 299, 351, 78 | 417, 168, 352, 280, 411, 325, 319, 320, 295, 296, 336, 319, 403, 404, 330, 79 | 348, 349, 293, 298, 333, 323, 454, 447, 15, 16, 315, 358, 429, 279, 14, 15, 80 | 316, 285, 336, 9, 329, 349, 350, 374, 380, 252, 318, 402, 403, 6, 197, 419, 81 | 318, 319, 325, 367, 364, 365, 435, 367, 397, 344, 438, 439, 272, 271, 311, 82 | 195, 5, 281, 273, 287, 291, 396, 428, 199, 311, 271, 268, 283, 444, 445, 83 | 373, 254, 339, 263, 466, 249, 282, 334, 296, 449, 347, 346, 264, 447, 454, 84 | 336, 296, 299, 338, 10, 151, 278, 439, 455, 292, 407, 415, 358, 371, 355, 85 | 340, 345, 372, 390, 249, 466, 346, 347, 280, 442, 443, 282, 19, 94, 370, 86 | 441, 442, 295, 248, 419, 197, 263, 255, 359, 440, 275, 274, 300, 383, 368, 87 | 351, 412, 465, 263, 467, 466, 301, 368, 389, 380, 374, 386, 395, 378, 379, 88 | 412, 351, 419, 436, 426, 322, 373, 390, 388, 2, 164, 393, 370, 462, 461, 89 | 164, 0, 267, 302, 11, 12, 374, 373, 387, 268, 12, 13, 293, 300, 301, 446, 90 | 261, 340, 385, 384, 381, 330, 266, 425, 426, 423, 391, 429, 355, 437, 391, 91 | 327, 326, 440, 457, 438, 341, 382, 362, 459, 457, 461, 434, 430, 394, 414, 92 | 463, 362, 396, 369, 262, 354, 461, 457, 316, 403, 402, 315, 404, 403, 314, 93 | 405, 404, 313, 406, 405, 421, 418, 406, 366, 401, 361, 306, 408, 407, 291, 94 | 409, 408, 287, 410, 409, 432, 436, 410, 434, 416, 411, 264, 368, 383, 309, 95 | 438, 457, 352, 376, 401, 274, 275, 4, 421, 428, 262, 294, 327, 358, 433, 96 | 416, 367, 289, 455, 439, 462, 370, 326, 2, 326, 370, 305, 460, 455, 254, 97 | 449, 448, 255, 261, 446, 253, 450, 449, 252, 451, 450, 256, 452, 451, 341, 98 | 453, 452, 413, 464, 463, 441, 413, 414, 258, 442, 441, 257, 443, 442, 259, 99 | 444, 443, 260, 445, 444, 467, 342, 445, 459, 458, 250, 289, 392, 290, 290, 100 | 328, 460, 376, 433, 435, 250, 290, 392, 411, 416, 433, 341, 463, 464, 453, 101 | 464, 465, 357, 465, 412, 343, 412, 399, 360, 363, 440, 437, 399, 456, 420, 102 | 456, 363, 401, 435, 288, 372, 383, 353, 339, 255, 249, 448, 261, 255, 133, 103 | 243, 190, 133, 155, 112, 33, 246, 247, 33, 130, 25, 398, 384, 286, 362, 398, 104 | 414, 362, 463, 341, 263, 359, 467, 263, 249, 255, 466, 467, 260, 75, 60, 105 | 166, 238, 239, 79, 162, 127, 139, 72, 11, 37, 121, 232, 120, 73, 72, 39, 106 | 114, 128, 47, 233, 232, 128, 103, 104, 67, 152, 175, 148, 173, 157, 155, 107 | 119, 118, 101, 74, 73, 40, 107, 9, 108, 49, 48, 131, 32, 194, 211, 184, 74, 108 | 185, 191, 80, 183, 185, 40, 186, 119, 230, 118, 210, 202, 214, 84, 83, 17, 109 | 77, 76, 146, 161, 160, 30, 190, 56, 173, 182, 106, 194, 138, 135, 192, 129, 110 | 203, 98, 54, 21, 68, 5, 51, 4, 145, 144, 23, 90, 77, 91, 207, 205, 187, 83, 111 | 201, 18, 181, 91, 182, 180, 90, 181, 16, 85, 17, 205, 206, 36, 176, 148, 112 | 140, 165, 92, 39, 245, 193, 244, 27, 159, 28, 30, 247, 161, 174, 236, 196, 113 | 103, 54, 104, 55, 193, 8, 111, 117, 31, 221, 189, 55, 240, 98, 99, 142, 126, 114 | 100, 219, 166, 218, 112, 155, 26, 198, 209, 131, 169, 135, 150, 114, 47, 115 | 217, 224, 223, 53, 220, 45, 134, 32, 211, 140, 109, 67, 108, 146, 43, 91, 116 | 231, 230, 120, 113, 226, 247, 105, 63, 52, 241, 238, 242, 124, 46, 156, 95, 117 | 78, 96, 70, 46, 63, 116, 143, 227, 116, 123, 111, 1, 44, 19, 3, 236, 51, 118 | 207, 216, 205, 26, 154, 22, 165, 39, 167, 199, 200, 208, 101, 36, 100, 43, 119 | 57, 202, 242, 20, 99, 56, 28, 157, 124, 35, 113, 29, 160, 27, 211, 204, 210, 120 | 124, 113, 46, 106, 43, 204, 96, 62, 77, 227, 137, 116, 73, 41, 72, 36, 203, 121 | 142, 235, 64, 240, 48, 49, 64, 42, 41, 74, 214, 212, 207, 183, 42, 184, 210, 122 | 169, 211, 140, 170, 176, 104, 105, 69, 193, 122, 168, 50, 123, 187, 89, 96, 123 | 90, 66, 65, 107, 179, 89, 180, 119, 101, 120, 68, 63, 104, 234, 93, 227, 16, 124 | 15, 85, 209, 129, 49, 15, 14, 86, 107, 55, 9, 120, 100, 121, 153, 145, 22, 125 | 178, 88, 179, 197, 6, 196, 89, 88, 96, 135, 138, 136, 138, 215, 172, 218, 126 | 115, 219, 41, 42, 81, 5, 195, 51, 57, 43, 61, 208, 171, 199, 41, 81, 38, 127 | 224, 53, 225, 24, 144, 110, 105, 52, 66, 118, 229, 117, 227, 34, 234, 66, 128 | 107, 69, 10, 109, 151, 219, 48, 235, 183, 62, 191, 142, 129, 126, 116, 111, 129 | 143, 7, 163, 246, 118, 117, 50, 223, 222, 52, 94, 19, 141, 222, 221, 65, 130 | 196, 3, 197, 45, 220, 44, 156, 70, 139, 188, 122, 245, 139, 71, 162, 145, 131 | 153, 159, 149, 170, 150, 122, 188, 196, 206, 216, 92, 163, 144, 161, 164, 2, 132 | 167, 242, 141, 241, 0, 164, 37, 11, 72, 12, 144, 145, 160, 12, 38, 13, 70, 133 | 63, 71, 31, 226, 111, 157, 158, 154, 36, 101, 205, 203, 206, 165, 126, 209, 134 | 217, 98, 165, 97, 237, 220, 218, 237, 239, 241, 210, 214, 169, 140, 171, 32, 135 | 241, 125, 237, 179, 86, 178, 180, 85, 179, 181, 84, 180, 182, 83, 181, 194, 136 | 201, 182, 177, 137, 132, 184, 76, 183, 185, 61, 184, 186, 57, 185, 216, 212, 137 | 186, 192, 214, 187, 139, 34, 156, 218, 79, 237, 147, 123, 177, 45, 44, 4, 138 | 208, 201, 32, 98, 64, 129, 192, 213, 138, 235, 59, 219, 141, 242, 97, 97, 2, 139 | 141, 240, 75, 235, 229, 24, 228, 31, 25, 226, 230, 23, 229, 231, 22, 230, 140 | 232, 26, 231, 233, 112, 232, 244, 189, 243, 189, 221, 190, 222, 28, 221, 141 | 223, 27, 222, 224, 29, 223, 225, 30, 224, 113, 247, 225, 99, 60, 240, 213, 142 | 147, 215, 60, 20, 166, 192, 187, 213, 243, 112, 244, 244, 233, 245, 245, 143 | 128, 188, 188, 114, 174, 134, 131, 220, 174, 217, 236, 236, 198, 134, 215, 144 | 177, 58, 156, 143, 124, 25, 110, 7, 31, 228, 25, 264, 356, 368, 0, 11, 267, 145 | 451, 452, 349, 267, 302, 269, 350, 357, 277, 350, 452, 357, 299, 333, 297, 146 | 396, 175, 377, 381, 384, 382, 280, 347, 330, 269, 303, 270, 151, 9, 337, 147 | 344, 278, 360, 424, 418, 431, 270, 304, 409, 272, 310, 407, 322, 270, 410, 148 | 449, 450, 347, 432, 422, 434, 18, 313, 17, 291, 306, 375, 259, 387, 260, 149 | 424, 335, 418, 434, 364, 416, 391, 423, 327, 301, 251, 298, 275, 281, 4, 150 | 254, 373, 253, 375, 307, 321, 280, 425, 411, 200, 421, 18, 335, 321, 406, 151 | 321, 320, 405, 314, 315, 17, 423, 426, 266, 396, 377, 369, 270, 322, 269, 152 | 413, 417, 464, 385, 386, 258, 248, 456, 419, 298, 284, 333, 168, 417, 8, 153 | 448, 346, 261, 417, 413, 285, 326, 327, 328, 277, 355, 329, 309, 392, 438, 154 | 381, 382, 256, 279, 429, 360, 365, 364, 379, 355, 277, 437, 282, 443, 283, 155 | 281, 275, 363, 395, 431, 369, 299, 297, 337, 335, 273, 321, 348, 450, 349, 156 | 359, 446, 467, 283, 293, 282, 250, 458, 462, 300, 276, 383, 292, 308, 325, 157 | 283, 276, 293, 264, 372, 447, 346, 352, 340, 354, 274, 19, 363, 456, 281, 158 | 426, 436, 425, 380, 381, 252, 267, 269, 393, 421, 200, 428, 371, 266, 329, 159 | 432, 287, 422, 290, 250, 328, 385, 258, 384, 446, 265, 342, 386, 387, 257, 160 | 422, 424, 430, 445, 342, 276, 422, 273, 424, 306, 292, 307, 352, 366, 345, 161 | 268, 271, 302, 358, 423, 371, 327, 294, 460, 331, 279, 294, 303, 271, 304, 162 | 436, 432, 427, 304, 272, 408, 395, 394, 431, 378, 395, 400, 296, 334, 299, 163 | 6, 351, 168, 376, 352, 411, 307, 325, 320, 285, 295, 336, 320, 319, 404, 164 | 329, 330, 349, 334, 293, 333, 366, 323, 447, 316, 15, 315, 331, 358, 279, 165 | 317, 14, 316, 8, 285, 9, 277, 329, 350, 253, 374, 252, 319, 318, 403, 351, 166 | 6, 419, 324, 318, 325, 397, 367, 365, 288, 435, 397, 278, 344, 439, 310, 167 | 272, 311, 248, 195, 281, 375, 273, 291, 175, 396, 199, 312, 311, 268, 276, 168 | 283, 445, 390, 373, 339, 295, 282, 296, 448, 449, 346, 356, 264, 454, 337, 169 | 336, 299, 337, 338, 151, 294, 278, 455, 308, 292, 415, 429, 358, 355, 265, 170 | 340, 372, 388, 390, 466, 352, 346, 280, 295, 442, 282, 354, 19, 370, 285, 171 | 441, 295, 195, 248, 197, 457, 440, 274, 301, 300, 368, 417, 351, 465, 251, 172 | 301, 389, 385, 380, 386, 394, 395, 379, 399, 412, 419, 410, 436, 322, 387, 173 | 373, 388, 326, 2, 393, 354, 370, 461, 393, 164, 267, 268, 302, 12, 386, 374, 174 | 387, 312, 268, 13, 298, 293, 301, 265, 446, 340, 380, 385, 381, 280, 330, 175 | 425, 322, 426, 391, 420, 429, 437, 393, 391, 326, 344, 440, 438, 458, 459, 176 | 461, 364, 434, 394, 428, 396, 262, 274, 354, 457, 317, 316, 402, 316, 315, 177 | 403, 315, 314, 404, 314, 313, 405, 313, 421, 406, 323, 366, 361, 292, 306, 178 | 407, 306, 291, 408, 291, 287, 409, 287, 432, 410, 427, 434, 411, 372, 264, 179 | 383, 459, 309, 457, 366, 352, 401, 1, 274, 4, 418, 421, 262, 331, 294, 358, 180 | 435, 433, 367, 392, 289, 439, 328, 462, 326, 94, 2, 370, 289, 305, 455, 339, 181 | 254, 448, 359, 255, 446, 254, 253, 449, 253, 252, 450, 252, 256, 451, 256, 182 | 341, 452, 414, 413, 463, 286, 441, 414, 286, 258, 441, 258, 257, 442, 257, 183 | 259, 443, 259, 260, 444, 260, 467, 445, 309, 459, 250, 305, 289, 290, 305, 184 | 290, 460, 401, 376, 435, 309, 250, 392, 376, 411, 433, 453, 341, 464, 357, 185 | 453, 465, 343, 357, 412, 437, 343, 399, 344, 360, 440, 420, 437, 456, 360, 186 | 420, 363, 361, 401, 288, 265, 372, 353, 390, 339, 249, 339, 448, 255]; 187 | 188 | module.exports= {TRIANGULATION} -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.19.0", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 24 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 25 | "requires": { 26 | "bytes": "3.1.0", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "~1.1.2", 30 | "http-errors": "1.7.2", 31 | "iconv-lite": "0.4.24", 32 | "on-finished": "~2.3.0", 33 | "qs": "6.7.0", 34 | "raw-body": "2.4.0", 35 | "type-is": "~1.6.17" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.1.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 41 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.3", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 46 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 47 | "requires": { 48 | "safe-buffer": "5.1.2" 49 | } 50 | }, 51 | "content-type": { 52 | "version": "1.0.4", 53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 54 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 55 | }, 56 | "cookie": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 59 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 60 | }, 61 | "cookie-signature": { 62 | "version": "1.0.6", 63 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 64 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 65 | }, 66 | "cors": { 67 | "version": "2.8.5", 68 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 69 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 70 | "requires": { 71 | "object-assign": "^4", 72 | "vary": "^1" 73 | } 74 | }, 75 | "debug": { 76 | "version": "2.6.9", 77 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 78 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 79 | "requires": { 80 | "ms": "2.0.0" 81 | } 82 | }, 83 | "depd": { 84 | "version": "1.1.2", 85 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 86 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 87 | }, 88 | "destroy": { 89 | "version": "1.0.4", 90 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 91 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 92 | }, 93 | "ee-first": { 94 | "version": "1.1.1", 95 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 96 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 97 | }, 98 | "encodeurl": { 99 | "version": "1.0.2", 100 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 101 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 102 | }, 103 | "escape-html": { 104 | "version": "1.0.3", 105 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 106 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 107 | }, 108 | "etag": { 109 | "version": "1.8.1", 110 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 111 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 112 | }, 113 | "express": { 114 | "version": "4.17.1", 115 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 116 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 117 | "requires": { 118 | "accepts": "~1.3.7", 119 | "array-flatten": "1.1.1", 120 | "body-parser": "1.19.0", 121 | "content-disposition": "0.5.3", 122 | "content-type": "~1.0.4", 123 | "cookie": "0.4.0", 124 | "cookie-signature": "1.0.6", 125 | "debug": "2.6.9", 126 | "depd": "~1.1.2", 127 | "encodeurl": "~1.0.2", 128 | "escape-html": "~1.0.3", 129 | "etag": "~1.8.1", 130 | "finalhandler": "~1.1.2", 131 | "fresh": "0.5.2", 132 | "merge-descriptors": "1.0.1", 133 | "methods": "~1.1.2", 134 | "on-finished": "~2.3.0", 135 | "parseurl": "~1.3.3", 136 | "path-to-regexp": "0.1.7", 137 | "proxy-addr": "~2.0.5", 138 | "qs": "6.7.0", 139 | "range-parser": "~1.2.1", 140 | "safe-buffer": "5.1.2", 141 | "send": "0.17.1", 142 | "serve-static": "1.14.1", 143 | "setprototypeof": "1.1.1", 144 | "statuses": "~1.5.0", 145 | "type-is": "~1.6.18", 146 | "utils-merge": "1.0.1", 147 | "vary": "~1.1.2" 148 | } 149 | }, 150 | "finalhandler": { 151 | "version": "1.1.2", 152 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 153 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 154 | "requires": { 155 | "debug": "2.6.9", 156 | "encodeurl": "~1.0.2", 157 | "escape-html": "~1.0.3", 158 | "on-finished": "~2.3.0", 159 | "parseurl": "~1.3.3", 160 | "statuses": "~1.5.0", 161 | "unpipe": "~1.0.0" 162 | } 163 | }, 164 | "forwarded": { 165 | "version": "0.1.2", 166 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 167 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 168 | }, 169 | "fresh": { 170 | "version": "0.5.2", 171 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 172 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 173 | }, 174 | "http-errors": { 175 | "version": "1.7.2", 176 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 177 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 178 | "requires": { 179 | "depd": "~1.1.2", 180 | "inherits": "2.0.3", 181 | "setprototypeof": "1.1.1", 182 | "statuses": ">= 1.5.0 < 2", 183 | "toidentifier": "1.0.0" 184 | } 185 | }, 186 | "iconv-lite": { 187 | "version": "0.4.24", 188 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 189 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 190 | "requires": { 191 | "safer-buffer": ">= 2.1.2 < 3" 192 | } 193 | }, 194 | "inherits": { 195 | "version": "2.0.3", 196 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 197 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 198 | }, 199 | "ipaddr.js": { 200 | "version": "1.9.1", 201 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 202 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 203 | }, 204 | "media-typer": { 205 | "version": "0.3.0", 206 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 207 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 208 | }, 209 | "merge-descriptors": { 210 | "version": "1.0.1", 211 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 212 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 213 | }, 214 | "methods": { 215 | "version": "1.1.2", 216 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 217 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 218 | }, 219 | "mime": { 220 | "version": "1.6.0", 221 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 222 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 223 | }, 224 | "mime-db": { 225 | "version": "1.43.0", 226 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", 227 | "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" 228 | }, 229 | "mime-types": { 230 | "version": "2.1.26", 231 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", 232 | "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", 233 | "requires": { 234 | "mime-db": "1.43.0" 235 | } 236 | }, 237 | "ms": { 238 | "version": "2.0.0", 239 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 240 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 241 | }, 242 | "negotiator": { 243 | "version": "0.6.2", 244 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 245 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 246 | }, 247 | "object-assign": { 248 | "version": "4.1.1", 249 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 250 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 251 | }, 252 | "on-finished": { 253 | "version": "2.3.0", 254 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 255 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 256 | "requires": { 257 | "ee-first": "1.1.1" 258 | } 259 | }, 260 | "parseurl": { 261 | "version": "1.3.3", 262 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 263 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 264 | }, 265 | "path-to-regexp": { 266 | "version": "0.1.7", 267 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 268 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 269 | }, 270 | "proxy-addr": { 271 | "version": "2.0.6", 272 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 273 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 274 | "requires": { 275 | "forwarded": "~0.1.2", 276 | "ipaddr.js": "1.9.1" 277 | } 278 | }, 279 | "qs": { 280 | "version": "6.7.0", 281 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 282 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 283 | }, 284 | "range-parser": { 285 | "version": "1.2.1", 286 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 287 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 288 | }, 289 | "raw-body": { 290 | "version": "2.4.0", 291 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 292 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 293 | "requires": { 294 | "bytes": "3.1.0", 295 | "http-errors": "1.7.2", 296 | "iconv-lite": "0.4.24", 297 | "unpipe": "1.0.0" 298 | } 299 | }, 300 | "safe-buffer": { 301 | "version": "5.1.2", 302 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 303 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 304 | }, 305 | "safer-buffer": { 306 | "version": "2.1.2", 307 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 308 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 309 | }, 310 | "send": { 311 | "version": "0.17.1", 312 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 313 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 314 | "requires": { 315 | "debug": "2.6.9", 316 | "depd": "~1.1.2", 317 | "destroy": "~1.0.4", 318 | "encodeurl": "~1.0.2", 319 | "escape-html": "~1.0.3", 320 | "etag": "~1.8.1", 321 | "fresh": "0.5.2", 322 | "http-errors": "~1.7.2", 323 | "mime": "1.6.0", 324 | "ms": "2.1.1", 325 | "on-finished": "~2.3.0", 326 | "range-parser": "~1.2.1", 327 | "statuses": "~1.5.0" 328 | }, 329 | "dependencies": { 330 | "ms": { 331 | "version": "2.1.1", 332 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 333 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 334 | } 335 | } 336 | }, 337 | "serve-static": { 338 | "version": "1.14.1", 339 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 340 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 341 | "requires": { 342 | "encodeurl": "~1.0.2", 343 | "escape-html": "~1.0.3", 344 | "parseurl": "~1.3.3", 345 | "send": "0.17.1" 346 | } 347 | }, 348 | "setprototypeof": { 349 | "version": "1.1.1", 350 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 351 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 352 | }, 353 | "statuses": { 354 | "version": "1.5.0", 355 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 356 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 357 | }, 358 | "toidentifier": { 359 | "version": "1.0.0", 360 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 361 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 362 | }, 363 | "type-is": { 364 | "version": "1.6.18", 365 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 366 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 367 | "requires": { 368 | "media-typer": "0.3.0", 369 | "mime-types": "~2.1.24" 370 | } 371 | }, 372 | "unpipe": { 373 | "version": "1.0.0", 374 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 375 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 376 | }, 377 | "utils-merge": { 378 | "version": "1.0.1", 379 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 380 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 381 | }, 382 | "vary": { 383 | "version": "1.1.2", 384 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 385 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 386 | } 387 | } 388 | } 389 | --------------------------------------------------------------------------------