JSFiddle - React, Tailwind, and code Playground
HTML
<button>Grayscale</button>
<button>Sepia</button>
<button>Red+Green+Blue</button>
<button>HSL</button>
<button>Plasma</button>
<button>Plasma++</button>
<label><input type="checkbox" disabled /><span>Run animation</span></label>
<script>
function buttonClick(e) {
if (document._anim) document._anim.stop();
document._anim = null;
requestAnimationFrame(() => {
switch (e.target.textContent) {
case 'Grayscale': render_grayscale(); break;
case 'Sepia': render_sepia(); break;
case 'Red+Green+Blue': render_rgb(); break;
case 'HSL': document._anim = render_hsl(); break;
case 'Plasma': document._anim = render_plasma(); break;
case 'Plasma++': document._anim = render_plasma_plus(); break;
}
let checkbox = document.querySelector('input');
checkbox.checked = !!document._anim;
checkbox.disabled = !document._anim;
});
}
document.querySelectorAll('button').forEach(b => b.onclick = buttonClick);
document.querySelector('input').onchange = e => {
if (document._anim) {
if (e.target.checked) document._anim.run();
else document._anim.stop();
}
}
</script>
<hr />
<canvas />
CSS
body {
background: #125616;
}
span {
color: #eaae34;
}
input:disabled + span {
color: #888;
}
JavaScript
function diamondSquare(magnitude, roughness) {
const size = Math.pow(2, magnitude) + 1;
const data = new Float32Array(size * size);
const idx = (x, y) => x + y * size;
let factor = 1;
const rnd = () => (Math.random() * 2 - 1) * factor;
const clamp = v => Math.max(-1, Math.min(1, v));
let currentDistance = size - 1;
data[0] = rnd(); data[currentDistance * size] = rnd();
data[currentDistance] = rnd(); data[size * size - 1] = rnd();
do {
let halfDistance = currentDistance / 2;
for (let offsetX = halfDistance; offsetX < size; offsetX += currentDistance) {
for (let offsetY = halfDistance; offsetY < size; offsetY += currentDistance) {
let sum =
data[idx(offsetX - halfDistance, offsetY - halfDistance)] +
data[idx(offsetX - halfDistance, offsetY + halfDistance)] +
data[idx(offsetX + halfDistance, offsetY - halfDistance)] +
data[idx(offsetX + halfDistance, offsetY + halfDistance)];
data[idx(offsetX, offsetY)] = clamp(sum / 4 + rnd());
}
}
for (let offset1 = 0; offset1 < size; offset1 += currentDistance) {
for (let offset2 = halfDistance; offset2 < size; offset2 += currentDistance) {
let count1 = 2, count2 = 2,
sum1 = data[idx(offset1, offset2 - halfDistance)] +
data[idx(offset1, offset2 + halfDistance)],
sum2 = data[idx(offset2 - halfDistance, offset1)] +
data[idx(offset2 + halfDistance, offset1)];
if (offset1 > 0) {
sum1 += data[idx(offset1 - halfDistance, offset2)]; count1++;
sum2 += data[idx(offset2, offset1 - halfDistance)]; count2++;
}
else if (offset1 < size - 1) {
sum1 += data[idx(offset1 + halfDistance, offset2)]; count1++;
sum2 += data[idx(offset2, offset1 + halfDistance)]; count2++;
}
data[idx(offset1, offset2)] = clamp(sum1 / count1 + rnd());
data[idx(offset2, offset1)] = clamp(sum2 / count2 + rnd());
}
}
factor *= Math.pow(2, -roughness);
} while ((currentDistance /= 2) > 1);
return data;
}
function...