<!DOCTYPE html>
<br> body {<br> margin: 0;<br> background: #111;<br> display: flex;<br> flex-direction: column;<br> align-items: center;<br> justify-content: center;<br> min-height: 100vh;<br> color: #fff;<br> font-family: sans-serif;<br> }<br> canvas {<br> border: 2px solid #333;<br> background: #222;<br> border-radius: 8px;<br> }<br> button {<br> margin-top: 15px;<br> padding: 10px 20px;<br> font-size: 16px;<br> cursor: pointer;<br> border: none;<br> background-color: #ff5722;<br> color: white;<br> border-radius: 4px;<br> }<br>
Start Animation & Sound
<br>
const canvas = document.getElementById('canvas');<br>
const ctx = canvas.getContext('2d');</p>
<div class="highlight"><pre class="highlight plaintext"><code>// Web Audio Context setup
let audioCtx;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
// Audio Synthesizers
function playBark() {
initAudio();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(300, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(120, audioCtx.currentTime + 0.15);
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.15);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.15);
}
function playFireSound() {
initAudio();
const bufferSize = audioCtx.sampleRate * 1.5;
const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
const output = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1; // White noise
}
const whiteNoise = audioCtx.createBufferSource();
whiteNoise.buffer = buffer;
const filter = audioCtx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(800, audioCtx.currentTime);
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0.4, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 1.5);
whiteNoise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
whiteNoise.start();
}
function playWhooshSound() {
initAudio();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(150, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(400, audioCtx.currentTime + 0.8);
gain.gain.setValueAtTime(0.2, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.8);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.8);
}
// Animation State Variables
let state = 'SLEEPING'; // SLEEPING, WAKE, FIRE, WINGS, FLYING
let dogX = 150, dogY = 280;
let wingSize = 0;
let particles = [];
let eyeOpen = false;
function drawDog() {
ctx.save();
ctx.translate(dogX, dogY);
// Tail
ctx.strokeStyle = '#d7ccc8';
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-35, -5);
ctx.quadraticCurveTo(-50, -20, -45, -35);
ctx.stroke();
// Dog Body
ctx.fillStyle = '#8d6e63';
ctx.beginPath();
ctx.ellipse(0, 0, 40, 25, 0, 0, Math.PI * 2);
ctx.fill();
// Dog Head
ctx.beginPath();
ctx.arc(35, -20, 20, 0, Math.PI * 2);
ctx.fill();
// Dog Snout
ctx.beginPath();
ctx.ellipse(50, -15, 10, 8, 0, 0, Math.PI * 2);
ctx.fill();
// Dog Nose
ctx.fillStyle = '#3e2723';
ctx.beginPath();
ctx.arc(58, -17, 3, 0, Math.PI * 2);
ctx.fill();
// Dog Ears
ctx.beginPath();
ctx.ellipse(25, -35, 6, 14, Math.PI / 6, 0, Math.PI * 2);
ctx.fill();
// Dog Eyes
ctx.fillStyle = '#000';
if (eyeOpen) {
ctx.beginPath();
ctx.arc(40, -23, 3, 0, Math.PI * 2);
ctx.fill();
} else {
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(37, -23);
ctx.lineTo(43, -23);
ctx.stroke();
}
// Wings (If growing or grown)
if (wingSize > 0) {
ctx.fillStyle = '#b71c1c';
// Left Wing
ctx.beginPath();
ctx.moveTo(-10, -15);
ctx.quadraticCurveTo(-10 - wingSize, -15 - wingSize, -30 - wingSize, -5 - wingSize);
ctx.quadraticCurveTo(-15, -5, -10, -15);
ctx.fill();
// Right Wing
ctx.beginPath();
ctx.moveTo(0, -15);
ctx.quadraticCurveTo(0 + wingSize / 2, -15 - wingSize, -20 + wingSize, -5 - wingSize);
ctx.quadraticCurveTo(-5, -5, 0, -15);
ctx.fill();
}
ctx.restore();
}
function createFireParticle() {
particles.push({
x: dogX + 58,
y: dogY - 17,
vx: 4 + Math.random() * 4,
vy: (Math.random() - 0.5) * 3,
size: 10 + Math.random() * 10,
life: 1,
color: `hsl(${Math.random() * 40}, 100%, 50%)`
});
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.life -= 0.03;
p.size *= 0.95;
ctx.fillStyle = p.color;
ctx.globalAlpha = Math.max(0, p.life);
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1.0;
if (p.life <= 0) particles.splice(i, 1);
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Floor
ctx.fillStyle = '#333';
ctx.fillRect(0, 310, canvas.width, 90);
drawDog();
updateParticles();
// State Handling
if (state === 'FIRE') {
createFireParticle();
} else if (state === 'WINGS') {
if (wingSize < 45) wingSize += 0.8;
} else if (state === 'FLYING') {
dogX += 2;
dogY -= 2;
}
requestAnimationFrame(animate);
}
function startSequence() {
// Reset
state = 'SLEEPING';
dogX = 150;
dogY = 280;
wingSize = 0;
eyeOpen = false;
// Timeline Sequence
setTimeout(() => {
state = 'WAKE';
eyeOpen = true;
playBark();
}, 1000);
setTimeout(() => {
state = 'FIRE';
playFireSound();
}, 2000);
setTimeout(() => {
state = 'WINGS';
}, 3800);
setTimeout(() => {
state = 'FLYING';
playWhooshSound();
}, 5000);
}
// Start loop
animate();
</code></pre></div>
<p>
Top comments (0)