Nanfeng

Notes on software development, code, and curious ideas

HTML Canvas Lantern Festival Greeting Animation with Custom Text

The Lantern Festival marks the end of traditional Lunar New Year celebrations. This small Canvas animation creates floating lanterns and displays a festival greeting without external libraries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Happy Lantern Festival</title>
<style>
html, body, canvas { width: 100%; height: 100%; margin: 0; }
body { overflow: hidden; background: #10051e; }
h1 { position: fixed; inset: 1.5rem 0 auto; z-index: 1; margin: 0;
color: #ffd86b; text-align: center; font-family: system-ui, sans-serif; }
</style>
</head>
<body>
<h1>Happy Lantern Festival</h1>
<canvas id="scene"></canvas>
<script>
const canvas = document.querySelector("#scene");
const ctx = canvas.getContext("2d");
const lanterns = Array.from({ length: 24 }, () => ({
x: Math.random(), y: Math.random(), size: 12 + Math.random() * 18,
speed: .00002 + Math.random() * .00004
}));

function resize() {
const ratio = devicePixelRatio || 1;
canvas.width = innerWidth * ratio;
canvas.height = innerHeight * ratio;
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
}

function frame(time) {
ctx.clearRect(0, 0, innerWidth, innerHeight);
for (const light of lanterns) {
light.y = (light.y - light.speed * 16 + 1) % 1;
const x = light.x * innerWidth + Math.sin(time / 900 + light.x * 8) * 10;
const y = light.y * innerHeight;
ctx.shadowBlur = 18;
ctx.shadowColor = "#ff9a35";
ctx.fillStyle = "#ef4938";
ctx.fillRect(x, y, light.size, light.size * 1.25);
}
requestAnimationFrame(frame);
}
addEventListener("resize", resize);
resize();
requestAnimationFrame(frame);
</script>
</body>
</html>

Save it as an HTML file and open it in a modern browser. Reduce the lantern count on low-powered devices, and add a prefers-reduced-motion alternative if the animation will be published as part of a larger site.

+