UNPKG

kampos

Version:

Tiny and fast effects compositor on WebGL

416 lines (375 loc) 12.8 kB
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta http-equiv="cache-control" content="no-cache" /> <title>kampos | Tiny and fast effects compositor on WebGL</title> <link href="https://fonts.googleapis.com/css?family=Nova+Round&display=swap" rel="stylesheet" /> <script async defer src="https://buttons.github.io/buttons.js"></script> <style> body { min-height: 100vh; margin: 0; background-image: linear-gradient( -45deg, peachpuff, palegreen, plum, peachpuff, palegreen, plum ), linear-gradient( 45deg, purple, darkblue, rebeccapurple, purple, darkblue, rebeccapurple ), url('./kampos.svg'); background-blend-mode: screen; background-repeat: no-repeat; background-position: center; font-family: 'Nova round', cursive, fantasy; font-size: 16px; color: darkslateblue; } nav > ul { display: flex; width: 100vw; margin: 0; padding: 0; list-style: none; } nav li { flex: 1 auto; padding: 1rem 0.5rem; } nav li.nav-items { font-size: 16px; font-weight: bold; } nav a, nav a:visited { color: darkslateblue; } nav a:hover { color: darkslateblue; } main { margin: 30vh auto 0; text-align: center; } h1, h2 { color: #bada; } h1 { font-size: 96px; margin: 0; text-shadow: 0 6px 10px darkslateblue; } h2 { font-size: 28px; margin: 0; text-shadow: 0 4px 4px darkslateblue; } header > span { position: absolute; top: 50px; right: 50px; } .target-canvas { position: fixed; top: 0; left: 0; z-index: -2; pointer-events: none; } #target2 { left: 50%; } #target3 { position: fixed; top: 10vh; left: 5vw; z-index: -1; pointer-events: none; } #target4 { position: fixed; bottom: 10vh; right: 5vw; z-index: -1; pointer-events: none; } #video, #video2 { position: fixed; top: 0; content-visibility: hidden; } @media (min-width: 641px) { html, body { margin: 0; } main { width: -moz-fit-content; width: fit-content; } h1 { font-size: 128px; } h2 { font-size: 32px; } nav > ul { width: -moz-fit-content; width: fit-content; margin: 0 auto; } nav li.nav-items { font-size: 20px; padding: 1rem 2rem; } } </style> </head> <body> <header> <nav> <ul> <li class="nav-items"> <a href="./docs/">API Reference</a> </li> <li class="nav-items"> <a href="./demo/">Demos</a> </li> <li class="nav-items"> <a href="https://github.com/wix-incubator/kampos" >Source</a > </li> <li> <a class="github-button" href="https://github.com/wix-incubator/kampos" data-size="large" aria-label="Star wix/kampos on GitHub" >Star</a > </li> </ul> </nav> </header> <main> <canvas class="target-canvas" id="target"></canvas> <canvas class="target-canvas" id="target2"></canvas> <canvas id="target3"></canvas> <canvas id="target4"></canvas> <h1>kampos</h1> <h2>Tiny and fast effects compositor</h2> <video id="video" src="https://video.wixstatic.com/video/11062b_6ceb80744f45401caf9eb666caeb9887/480p/mp4/file.mp4" loop muted preload="auto" crossorigin="anonymous" playsinline autoplay ></video> <video id="video2" src="./demo/starry-night.mp4" loop muted preload="auto" crossorigin="anonymous" playsinline autoplay ></video> </main> </body> <script type="module"> import { Kampos, effects, noise } from './index.js'; const target = document.querySelector('#target'); const targetCtx = target.getContext('bitmaprenderer'); const target2 = document.querySelector('#target2'); const target2Ctx = target2.getContext('bitmaprenderer'); const target3 = document.querySelector('#target3'); const target3Ctx = target3.getContext('bitmaprenderer'); const target4 = document.querySelector('#target4'); const target4Ctx = target4.getContext('bitmaprenderer'); const body = document.body; const width = body.clientWidth / 2; const height = body.clientHeight; target.width = width; target.height = height; target2.width = width; target2.height = height; const offscreen = new OffscreenCanvas(width, height); const mousePosition = [0.5, 0.5]; const mousePosition2 = [0.5, 0.5]; const turbulence = effects.turbulence({ noise: noise.simplex, output: '\n', }); const getRender = (c, d, mouse) => ({ fragment: { uniform: { u_resolution: 'vec2', u_pointer: 'vec2', }, constant: ` vec3 A = vec3(0.5); vec3 B = vec3(0.5); vec3 C = vec3(${c}); vec3 D = vec3(${d}); vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) { return a + b * cos(6.28318 * (c * t + d)); }`, main: ` vec2 st = gl_FragCoord.xy / u_resolution.xy; float dist = distance(u_pointer, st); alpha = smoothstep(turbulenceValue * 0.2, 0.2 + turbulenceValue * 0.2, dist); color = palette(turbulenceValue, A, B, C, D);`, }, uniforms: [ { name: 'u_resolution', type: 'f', data: [width, height], }, { name: 'u_pointer', type: 'f', data: mouse, }, ], get mouse() { return this.uniforms[1].data; }, set mouse(data) { this.uniforms[1].data[0] = data[0]; this.uniforms[1].data[1] = data[1]; }, }); const render = getRender( [1.0, 0.7, 0.4], [0.0, 0.15, 0.2], mousePosition, ); const render2 = getRender( [0.0, 0.9, 0.6], [0.6, 0.15, 0.5], mousePosition2, ); const FREQUENCY = 2 / (width > height ? width : height); turbulence.frequency = { x: FREQUENCY, y: FREQUENCY }; turbulence.octaves = 6; const instance = new Kampos({ target: offscreen, effects: [turbulence, render], noSource: true, }); const instance2 = new Kampos({ target: offscreen, effects: [turbulence, render2], noSource: true, }); const source = { width, height }; instance.play( (time) => { instance.setSource(source, true); render.mouse = mousePosition; turbulence.time = time * 2; }, () => { const bitmap = offscreen.transferToImageBitmap(); targetCtx.transferFromImageBitmap(bitmap); }, ); instance2.play( (time) => { instance.setSource(source, true); render2.mouse = mousePosition2; turbulence.time = time * 3; }, () => { const bitmap = offscreen.transferToImageBitmap(); target2Ctx.transferFromImageBitmap(bitmap); }, ); document.body.addEventListener('mousemove', (e) => { mousePosition[0] = e.clientX / width; mousePosition2[0] = (e.clientX - width) / width; mousePosition[1] = (height - e.clientY) / height; mousePosition2[1] = (height - e.clientY) / height; }); const video = document.querySelector('#video'); video.addEventListener( 'timeupdate', () => { const duotone = effects.duotone(); const instance3 = new Kampos({ target: offscreen, effects: [duotone], }); const width = video.videoWidth; const height = video.videoHeight; const source = { media: video, width, height }; target3.width = width; target3.height = height; instance3.setSource(source); instance3.play( (time) => { instance3.setSource(source, true); }, () => { const bitmap = offscreen.transferToImageBitmap(); target3Ctx.transferFromImageBitmap(bitmap); }, ); }, { once: true }, ); const video2 = document.querySelector('#video2'); video2.addEventListener( 'timeupdate', () => { const duotone = effects.duotone({ light: [1, 1, 1, 1], dark: [0, 0, 0, 1], }); const instance4 = new Kampos({ target: offscreen, effects: [duotone], }); const width = video2.videoWidth; const height = video2.videoHeight; const source = { media: video2, width, height }; target4.width = width; target4.height = height; instance4.setSource(source); instance4.play( (time) => { instance4.setSource(source, true); }, () => { const bitmap = offscreen.transferToImageBitmap(); target4Ctx.transferFromImageBitmap(bitmap); }, ); }, { once: true }, ); </script> </html>