Skip to content

Instantly share code, notes, and snippets.

@Teemu
Created January 1, 2025 16:18
Show Gist options
  • Save Teemu/91903b5e538734443d9d75ccf0fc3c62 to your computer and use it in GitHub Desktop.
Save Teemu/91903b5e538734443d9d75ccf0fc3c62 to your computer and use it in GitHub Desktop.
Snowman DMC demo
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>#dmc Dancing Snowman by Teemu</title>
<style>
html,
body {
margin: 0;
padding: 0;
background: #000;
overflow: hidden;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<canvas id="demoCanvas" width="360" height="640"></canvas>
<script>
// Grab canvas and context
const canvas = document.getElementById("demoCanvas");
const ctx = canvas.getContext("2d");
const W = canvas.width;
const H = canvas.height;
// Adjust these for more/less swirling effect
const swirlFrequency = 0.002;
const swirlAmplitude = 80;
// Snowflake parameters
const maxSnowflakes = 100;
const snowflakes = [];
for (let i = 0; i < maxSnowflakes; i++) {
snowflakes.push({
x: Math.random() * W,
y: Math.random() * (H + 200) - 100,
r: 2 + Math.random() * 5,
speed: 1 + Math.random() * 1.5,
angle: Math.random() * 360, // For rotation
rotationSpeed: (Math.random() - 0.5) * 2, // Rotation speed in radians per frame
});
}
// Animate arms with a sine wave
let frameCount = 0;
/**
* Draw the swirling background
*/
function drawSwirlingBackground(time) {
const swirlFrequency = 0.002;
const swirlAmplitude = 80;
const gradient = ctx.createRadialGradient(
W / 2,
H / 2,
100,
W / 2 + swirlAmplitude * Math.sin(time * swirlFrequency),
H / 2 + swirlAmplitude * Math.cos(time * swirlFrequency),
Math.max(W, H),
);
gradient.addColorStop(0, "skyblue");
gradient.addColorStop(1, "dodgerblue");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, W, H);
}
function drawElements() {
// Example: Simple pine trees
const treeCount = 5;
for (let i = 0; i < treeCount; i++) {
const treeX = 90 + i * 40;
const treeY = H - 170;
drawTree(treeX, treeY, 30);
}
// Smaller trees
}
function drawTree(x, y, size) {
ctx.fillStyle = "#006400"; // Dark green
ctx.beginPath();
ctx.moveTo(x, y - size * 3);
ctx.lineTo(x - size, y);
ctx.lineTo(x + size, y);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(x, y - size * 2);
ctx.lineTo(x - size * 0.8, y + size * 0.2);
ctx.lineTo(x + size * 0.8, y + size * 0.2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(x, y - size);
ctx.lineTo(x - size * 0.6, y + size * 0.8);
ctx.lineTo(x + size * 0.6, y + size * 0.8);
ctx.closePath();
ctx.fill();
}
/**
* Draw falling snow with more complexity
*/
function drawSnow(frameCount) {
for (let i = 0; i < snowflakes.length; i++) {
const s = snowflakes[i];
ctx.save();
ctx.globalAlpha = s.opacity;
ctx.translate(s.x, s.y);
ctx.rotate((s.angle * Math.PI) / 180);
// Draw a more complex snowflake (e.g., a six-branch snowflake)
ctx.strokeStyle = "white";
ctx.lineWidth = 4;
for (let j = 0; j < 6; j++) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, -s.r * 2);
ctx.stroke();
ctx.rotate(Math.PI / 3);
}
ctx.restore();
// Update snowflake position and rotation
s.y += s.speed;
s.angle += s.rotationSpeed;
if (s.y > H + 100) {
s.y = -100;
s.x = Math.random() * W;
}
}
}
/**
* Draw dancing snowman
*/
function drawSnowman(x, y, size, time) {
const danceX = size > 30 ? 3 * 10 * Math.sin(time * 0.1) : 0;
const danceY = size > 30 ? 3 * 5 * Math.cos(time * 0.2) : 0;
ctx.save();
ctx.translate(danceX, danceY);
const bottomRadius = size;
const middleRadius = size * 0.7;
const headRadius = size * 0.5;
const bottomY = y;
const middleY = y - bottomRadius * 1.4;
const headY = middleY - middleRadius * 1.4;
// Body circles
ctx.beginPath();
ctx.arc(x, bottomY, bottomRadius, 0, 2 * Math.PI);
ctx.fillStyle = "white";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.fill();
ctx.beginPath();
ctx.arc(x, middleY, middleRadius, 0, 2 * Math.PI);
ctx.fillStyle = "white";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.fill();
ctx.beginPath();
ctx.arc(x, headY, headRadius, 0, 2 * Math.PI);
ctx.fillStyle = "white";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.fill();
// Eyes
ctx.fillStyle = "black";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
const eyeOffsetX = size * 0.2;
const eyeOffsetY = size * -0.1;
ctx.beginPath();
ctx.arc(
x - eyeOffsetX,
headY + eyeOffsetY,
size * 0.05,
0,
2 * Math.PI,
);
ctx.arc(
x + eyeOffsetX,
headY + eyeOffsetY,
size * 0.05,
0,
2 * Math.PI,
);
ctx.fill();
// Carrot nose
ctx.beginPath();
ctx.moveTo(x, headY);
ctx.lineTo(x + size * 0.3, headY + size * 0.1);
ctx.lineTo(x, headY + size * 0.1);
ctx.fillStyle = "orange";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.fill();
// Mouth
ctx.beginPath();
ctx.arc(x, headY + size * 0.15, size * 0.2, 0, Math.PI, false);
ctx.strokeStyle = "black";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.lineWidth = size * 0.02;
ctx.stroke();
// Arms
ctx.strokeStyle = "white";
if (size < 30)
ctx.strokeStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
ctx.lineWidth = size * 0.2;
ctx.lineCap = "round";
const leftArmAngle = -Math.PI / 4 + 0.6 * Math.sin(time * 0.1) + 4;
const leftArmLength = middleRadius * 1.5;
ctx.beginPath();
ctx.moveTo(x - middleRadius * 0.8, middleY);
ctx.lineTo(
x - middleRadius * 0.8 + leftArmLength * Math.cos(leftArmAngle),
middleY + leftArmLength * Math.sin(leftArmAngle),
);
ctx.stroke();
// Right arm
const rightArmAngle =
(-3 * Math.PI) / 4 + 0.6 * Math.sin(time * 0.1) - 4;
const rightArmLength = middleRadius * 1.5;
ctx.beginPath();
ctx.moveTo(x + middleRadius * 0.8, middleY);
ctx.lineTo(
x + middleRadius * 0.8 + rightArmLength * Math.cos(rightArmAngle),
middleY + rightArmLength * Math.sin(rightArmAngle),
);
ctx.stroke();
// Hat
const hatWidth = size * 0.8;
const hatHeight = size * 0.3;
const hatBrimWidth = size;
const hatBrimHeight = size * 0.05;
const hatY = headY - headRadius - hatHeight;
ctx.fillStyle = "black";
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`;
// Brim
ctx.fillRect(
x - hatBrimWidth / 2,
hatY + hatHeight,
hatBrimWidth,
hatBrimHeight,
);
// Top hat
ctx.fillRect(x - hatWidth / 2, hatY, hatWidth, hatHeight);
ctx.restore();
}
function drawSnowflakes() {
ctx.save();
ctx.fillStyle = "white";
for (let i = 0; i < snowflakes.length; i++) {
const s = snowflakes[i];
ctx.globalAlpha = s.opacity;
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, 2 * Math.PI);
ctx.fill();
s.y += s.speed;
if (s.y > H) {
s.y = -s.r;
s.x = Math.random() * W;
}
}
ctx.restore();
}
function drawGround() {
ctx.fillStyle = "white";
ctx.beginPath();
for (let i = 0; i < W + 20; i += 5) {
const heightVariation = Math.sin(i * 0.05 + frameCount * 0.02) * 10;
ctx.lineTo(i, H - 170 - heightVariation);
}
ctx.lineTo(W, H);
ctx.lineTo(0, H);
ctx.closePath();
ctx.fill();
}
function update() {
frameCount++;
drawSwirlingBackground(frameCount);
drawElements();
drawSnow(frameCount);
drawGround();
drawSnowman(W / 2, H * 0.7, 50, frameCount);
requestAnimationFrame(update);
}
// Start
update();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment