Last active
January 2, 2025 10:53
-
-
Save Garciat/3ab79e066cbcc9b7dff28348baf220a5 to your computer and use it in GitHub Desktop.
Ondulator // Simulates a hypnotizing animation I saw once, somewhere. Click to change the pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Ondulator</title> | |
<style>html,body{margin:0;height:100%;}</style> | |
<script> | |
const PI = Math.PI; | |
const TAU = 2 * PI; | |
const RR = 50; | |
let CW = 800; | |
let CH = 800; | |
function resize(canvas) { | |
CW = CH = 0.9 * Math.min(document.body.clientWidth, document.body.clientHeight); | |
canvas.width = CW; | |
canvas.height = CH; | |
} | |
function clear(ctx) { | |
ctx.save(); | |
ctx.translate(0, 0); | |
ctx.clearRect(0, 0, CW, CH); | |
ctx.fillStyle = 'black'; | |
ctx.fillRect(0, 0, CW, CH); | |
ctx.restore(); | |
} | |
function drawOndulator(ctx, color, width, radius, nodes, state, rotation) { | |
ctx.save(); | |
const sliceR = TAU / nodes; | |
const stepR = sliceR / RR; | |
const k = state * radius * Math.sin(sliceR / 2); | |
ctx.translate(CW/2, CH/2); | |
ctx.rotate(rotation); | |
ctx.lineWidth = width; | |
ctx.strokeStyle = color; | |
for (let i = 0; i < nodes; ++i) { | |
ctx.beginPath(); | |
ctx.moveTo(0, radius); | |
for (let j = 0; j < RR + 2; ++j) { | |
const r = radius + Math.pow(-1, i) * k * Math.sin(PI * j / RR); | |
const x = r * Math.sin(j * stepR); | |
const y = r * Math.cos(j * stepR); | |
ctx.lineTo(x, y); | |
} | |
ctx.stroke(); | |
ctx.rotate(sliceR); | |
} | |
ctx.restore(); | |
} | |
function main() { | |
const canvas = document.createElement('canvas'); | |
document.body.appendChild(canvas); | |
document.body.style.margin = '0px'; | |
document.body.style.display = 'flex'; | |
document.body.style.alignItems = 'center'; | |
document.body.style.justifyContent = 'center'; | |
const ctx = canvas.getContext('2d'); | |
const modes = 2; | |
let mode = 0; | |
canvas.addEventListener('click', () => { | |
mode = (mode + 1) % modes; | |
}); | |
function loop(t) { | |
clear(ctx); | |
ctx.globalCompositeOperation = 'lighten'; | |
switch (mode) { | |
case 0: | |
drawOndulator(ctx, 'rgba(255, 255, 255, 1)', 0.08 * CW / 2, 0.75 * CW / 2, 12, 0.5 * +Math.sin(t / 500), t/2500); | |
drawOndulator(ctx, 'rgba(255, 255, 255, 1)', 0.08 * CW / 2, 0.75 * CW / 2, 12, 0.5 * -Math.sin(t / 500), t/2500); | |
break; | |
case 1: | |
drawOndulator(ctx, 'rgba(255, 0, 0, 1)', 0.1 * CW / 2, 0.75 * CW / 2, 12, 0.5 * +Math.sin(t / 500), t/2500); | |
drawOndulator(ctx, 'rgba(0, 255, 255, 1)', 0.1 * CW / 2, 0.75 * CW / 2, 12, 0.5 * -Math.cos(t / 500), t/2500); | |
drawOndulator(ctx, 'rgba(0, 255, 0, 1)', 0.1 * CW / 2, 0.75 * CW / 2, 12, 0.5 * +Math.cos(t / 500), t/2500); | |
break; | |
} | |
requestAnimationFrame(loop); | |
} | |
resize(canvas); | |
requestAnimationFrame(loop); | |
} | |
window.addEventListener('load', main); | |
</script> | |
</head> | |
<body style="background-color: black"> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment