Part 3 of the HTML5 Canvas series — see the full series.
This is the second Canvas visualization I’m experimenting with. The first one was a Julia fractal animation.
This article is part of the JavaScript Canvas series where I post experiments, JavaScript generative art, visualizations, Canvas animations, code snippets and how-to’s.

Here are some simple waves generated using a sine function and Math.PI. Again, I won’t go into maths details or break down the code.
How the sine wave works
I said I won’t go deep into the maths, but it’s worth a quick word on why this works. A wave is just a sine value plotted across the canvas: as the x position moves from left to right, I feed that x into Math.sin() and use the result to set the y position. Because the sine function smoothly oscillates between -1 and 1, the points trace out that familiar rolling curve. Multiplying the sine result scales the height of the wave, and adding a steadily increasing offset to the angle on each frame is what makes the wave appear to travel across the screen.
The drawing itself is straightforward: I start a path, step through the x values in a loop, and use moveTo and lineTo to connect each point into a continuous line before stroking it. The animation runs on requestAnimationFrame, the browser’s modern, battery-friendly way to drive canvas animation, so the wave redraws in sync with the display’s refresh rate rather than on a fixed timer.
See the Pen Canvas: 2D Waves by Ciprian (@ciprian) on CodePen.
Here’s the JavaScript code:
<canvas id="canvas" height="500" width="500" style="border: 16px solid black; margin: 48px auto; display: block;"></canvas>
<script>
document.addEventListener('DOMContentLoaded', () => {
let canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
width = 500,
height = 500;
canvas.width = width;
canvas.height = height;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.strokeStyle = '#badc58';
ctx.lineWidth = 2;
let waves = [];
for (let w =18; w > 10; w-=0.5) {
waves.push({amp : (w * 0.05), freq : 0, move : 0});
}
function render() {
ctx.fillRect(0, 0, canvas.width, canvas.height);
for(w = 0; w < waves.length; w++) {
let i = w + 16,
wave = waves[w];
ctx.beginPath();
ctx.moveTo(width, Math.sin(wave.freq * Math.PI / i));
for (let x = width; x >=0; x--) {
let y = Math.sin(wave.freq * Math.PI / i / 2);
wave.freq += wave.amp;
ctx.lineTo(x, (w * 32) + y * 15);
}
wave.move += (i * 0.1);
wave.freq = wave.move;
ctx.stroke();
}
requestAnimationFrame(render);
}
render();
});
</script>
Give it a whirl!
← Previous: Canvas animation | Next: Shadows and gradient fill →