color-synth

a synth that generates colors instead of sounds
Log | Files | Refs | README

commit e8cdcb3c25d66e3a0744509c6d36d14e275a6012
parent f2d8c4e4e2b5d04751bdb69fa715b8809227576c
Author: massi <mdsiboldi@gmail.com>
Date:   Tue, 20 Jun 2023 20:59:30 -0700

basic visual synth

Diffstat:
Msrc/app.html | 10++++++++++
Asrc/routes/+error.svelte | 1+
Asrc/routes/+layout.svelte | 1+
Asrc/routes/+page.js | 1+
Msrc/routes/+page.svelte | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/src/app.html b/src/app.html @@ -10,3 +10,13 @@ <div style="display: contents">%sveltekit.body%</div> </body> </html> + + +<style> + html { + overflow: hidden; + } + body { + margin: auto; + } +</style> diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte @@ -0,0 +1 @@ +<h1>Doggone it</h1> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -0,0 +1 @@ +<slot /> diff --git a/src/routes/+page.js b/src/routes/+page.js @@ -0,0 +1 @@ +export const ssr = false; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte @@ -1,2 +1,106 @@ -<h1>Welcome to SvelteKit</h1> -<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p> +<script lang="ts"> + import { onMount } from 'svelte'; + let cvs: HTMLCanvasElement; + + onMount(() => { + const resizeObserver = new ResizeObserver((entries) => { + const entry = entries.find((entry) => entry.target === cvs); + if (entry) { + cvs.width = entry.devicePixelContentBoxSize[0].inlineSize; + cvs.height = entry.devicePixelContentBoxSize[0].blockSize; + } + }); + + resizeObserver.observe(cvs); + if (cvs) resizeObserver.observe(cvs, { box: 'device-pixel-content-box' }); + + // This callback cleans up the observer + return () => resizeObserver.unobserve(cvs); + }); + + let t = 0; + + function step() { + t = (t + 1) % 1000000000000; // lol + } + + type Binding = () => number; + + function mkOsc(inputs: { rate: Binding; amount: Binding }): Binding { + let { rate, amount } = inputs; + return () => (Math.sin(2 * Math.PI * t * rate()) - 0.5) * amount(); + } + + function combinatorBinding(...bindings: Binding[]): Binding { + return () => bindings.reduce((a, b) => a + b(), 0); + } + + function constBinding(value: number): Binding { + return () => value; + } + + function drawSquares(ctx: CanvasRenderingContext2D) { + if (!cvs) { + return; + } + const cols = 100; + const rows = 200; + let color = [128, 128, 128]; + let width = cvs.width; + let height = cvs.height; + let paneWidth = Math.ceil(width / cols); + let paneHeight = Math.ceil(height / rows); + + let osc1 = mkOsc({ + rate: combinatorBinding( + constBinding(0.761243), + mkOsc({ rate: () => 0.0001, amount: () => 0.01 }) + ), + amount: constBinding(30) + }); + let osc2 = mkOsc({ + rate: combinatorBinding( + mkOsc({ rate: () => 0.00001, amount: () => 1 }), + constBinding(0.881242) + ), + amount: constBinding(55) + }); + let osc3 = mkOsc({ rate: constBinding(0.000001), amount: constBinding(255) }); + + const red = combinatorBinding(constBinding(128), osc1); + const green = combinatorBinding(constBinding(128), osc2); + const blue = combinatorBinding(constBinding(128), osc3); + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + step(); + color[0] = red(); + color[1] = green(); + color[2] = blue(); + ctx.fillStyle = `rgb(${color.join(', ')})`; + let x = col * paneWidth; + let y = row * paneHeight; + ctx.fillRect(x, y, paneWidth, paneHeight); + } + } + } + function run() { + requestAnimationFrame((t) => { + drawSquares(cvs.getContext('2d')); + run(); + }); + } + + $: if (cvs) { + run(); + } +</script> + +<canvas bind:this={cvs} /> + +<style> + canvas { + width: 100vw; + height: 100vh; + background: salmon; + } +</style>