diff options
| author | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-05 12:42:44 -0700 |
|---|---|---|
| committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-05 12:42:44 -0700 |
| commit | 94b912b649150b9863a62096ba4740b2cc8ad21a (patch) | |
| tree | 1882236fc980fa5946d3089a2f2e4670f3a70580 /src/toys/julia | |
| parent | ab0ddcaeb1fc37351d14b89100e07d3a343ba9e1 (diff) | |
| download | lizdotcoffee-94b912b649150b9863a62096ba4740b2cc8ad21a.tar.gz lizdotcoffee-94b912b649150b9863a62096ba4740b2cc8ad21a.zip | |
Factor
Diffstat (limited to 'src/toys/julia')
| -rw-r--r-- | src/toys/julia/index.html | 51 | ||||
| -rw-r--r-- | src/toys/julia/julia.js | 136 |
2 files changed, 0 insertions, 187 deletions
diff --git a/src/toys/julia/index.html b/src/toys/julia/index.html deleted file mode 100644 index 9d9de51..0000000 --- a/src/toys/julia/index.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> - -<html> - <head> - <style> - body { - height: 100vh; - width: 100vw; - margin: 0; - } - .bottomRight { - padding: 12px; - position: fixed; - bottom: 0; - right: 0; - background-color: rgba(255,255,255,0.5); - border: 1px solid white; - border-radius: 8px; - margin-right: 6px; - margin-bottom: 6px; - } - .verticalSlider - { - writing-mode: bt-lr; /* IE */ - -webkit-appearance: slider-vertical; /* Chromium */ - width: 8px; - padding: 0 5px; - } - </style> - </head> - <body> - <div id="canvasHolder"> - - </div> - <div class="bottomRight"> - <input orient="vertical" type="range" min="-1000" max="1000" value="0" id="imaginarySlider" class="verticalSlider"> - <input type="range" min="-1000" max="1000" value="0" id="realSlider"> - <br> - imaginary: <input id="imag-val" size="10" type="number" step="0.001"></input> - <button id="animate-imag" onclick="startAnim('animate-imag', 'ci')">Animate</button> - <br> - real: <input id="real-val" size="10" type="number" step="0.001"></input> - <button id="animate-real" onclick="startAnim('animate-real', 'cr')">Animate</button> - <br> - powered by <a href="https://github.com/gpujs/gpu.js">gpu.js</a> - </div> - - <script src="https://cdn.jsdelivr.net/npm/gpu.js@latest/dist/gpu-browser.min.js"></script> - <script src="julia.js"></script> - </body> -</html> diff --git a/src/toys/julia/julia.js b/src/toys/julia/julia.js deleted file mode 100644 index 38bcd4d..0000000 --- a/src/toys/julia/julia.js +++ /dev/null @@ -1,136 +0,0 @@ -const MIN_ZOOM = 0.0001; -const MAX_ZOOM = 4; -const C_THRESHOLD = Math.sqrt(2)/2; -const SLIDER_DIV = 2000*C_THRESHOLD; - -const gpu = new GPU.GPU(); -const buildRender = (width, height) => gpu.createKernel(function (maxIterations, cr, ci, centerX, centerY, zoom, colorMultipliers) { - let zx = (this.output.x / this.output.y) * (centerX + (4 * this.thread.x / this.output.x - 2) * (zoom / 4)); - let zy = centerY + (4 * this.thread.y / this.output.y - 2) * (zoom / 4); - let iterations = 0; - for (let i = 0; i < maxIterations; i++) { - const xtemp = zx * zx - zy * zy + cr; - zy = 2 * zx * zy + ci; - zx = xtemp; - if (zx * zx + zy * zy > 4) { - iterations = i; - break; - } - } - if (iterations == 0 || iterations == maxIterations) { - this.color(0, 0, 0); - } else { - this.color(colorMultipliers[0] * iterations, colorMultipliers[1] * iterations, colorMultipliers[2] * iterations); - } -}, { output: [width, height], graphical: true }); - -const canvasHolder = document.getElementById('canvasHolder'); -let render; // The GPU kernel built from buildRender -let state = { - colorMultipliers: [0.01 * Math.random() + 0.015, 0.03 * Math.random() + 0.007, 0.02 * Math.random() + 0.010], - changes: { - centerX: 0, - centerY: 0, - zoom: 3, - cr: parseFloat(document.getElementById('realSlider').value) / SLIDER_DIV, - ci: parseFloat(document.getElementById('imaginarySlider').value) / SLIDER_DIV, - maxIterations: 1000, - width: document.body.clientWidth, - height: document.body.clientHeight, - }, -}; - -const doRender = (renderF, state) => { - // gpu.js doesn't support JS objects as kernel parameters - https://github.com/gpujs/gpu.js/issues/245 - renderF(state.maxIterations, state.cr, state.ci, state.centerX, state.centerY, state.zoom, state.colorMultipliers); -}; - -const loop = () => { - const stateChanges = Object.keys(state.changes); - if (stateChanges.length > 0) { - state = {...state, ...state.changes}; - if (state.changes.width || state.changes.height) { - render = buildRender(state.width, state.height); - canvasHolder.appendChild(render.canvas); - } - if (typeof state.changes.ci !== 'undefined') { - document.getElementById('imag-val').value = state.ci.toFixed(4); - document.getElementById('imaginarySlider').value = state.changes.ci * SLIDER_DIV; - } - if (typeof state.changes.cr !== 'undefined') { - document.getElementById('real-val').value = state.cr.toFixed(4); - document.getElementById('realSlider').value = state.changes.cr * SLIDER_DIV; - } - state.changes = {}; - doRender(render, state); - } - - requestAnimationFrame(loop); -}; - -loop(); - -// UI Code - -const startAnim = (sliderId, complexComponentName='ci') => { - const restart = (interval) => { - clearInterval(interval); - document.getElementById(sliderId).innerHTML = 'Animate'; - document.getElementById(sliderId).onclick = ()=>startAnim(sliderId, complexComponentName); - return; - }; - const start = setInterval(() => { - if (state[complexComponentName] >= C_THRESHOLD) { - restart(start); - } - - state.changes[complexComponentName] = state[complexComponentName] + 0.001; - }, 1000/60); - document.getElementById(sliderId).innerHTML = 'Stop'; - document.getElementById(sliderId).onclick = ()=>restart(start); -}; - -document.getElementById('imaginarySlider').oninput = function() { - state.changes.ci = parseFloat(this.value) / SLIDER_DIV; -}; -document.getElementById('realSlider').oninput = function() { - state.changes.cr = parseFloat(this.value) / SLIDER_DIV; -}; - -document.getElementById('imag-val').addEventListener('change', function (e) { - state.changes.ci = parseFloat(this.value); -}); -document.getElementById('real-val').addEventListener('change', function (e) { - state.changes.cr = parseFloat(this.value); -}); - -canvasHolder.addEventListener('wheel', (e) => { - e.preventDefault(); - state.changes.zoom = Math.min(Math.max(state.zoom + e.deltaY * 0.001 * state.zoom, MIN_ZOOM), MAX_ZOOM); -}); - -let isDown = false; -canvasHolder.addEventListener('mousedown', (e) => { - e.preventDefault(); - isDown = true; -}, true); - -canvasHolder.addEventListener('mouseup', (e) => { - e.preventDefault(); - isDown = false; -}, true); - -canvasHolder.addEventListener('mousemove', (e) => { - e.preventDefault(); - if (isDown) { - let deltaX = -e.movementX * state.zoom / document.body.clientWidth; - let deltaY = e.movementY * state.zoom / document.body.clientHeight; - state.changes.centerX = state.centerX + deltaX; - state.changes.centerY = state.centerY + deltaY; - } -}, true); - -window.addEventListener('resize', () => { - state.changes.width = document.body.clientWidth; - state.changes.height = document.body.clientHeight; -}); |
