Skip to content

Commit 635d580

Browse files
feat(theme): play Ender Dragon growl during fly-across (#307)
* feat(theme): play canonical Ender Dragon growl during fly-across Adds the dragon idle/growl SFX (~3s, ~26 KB MP3 transcoded from minecraft.wiki's Ender_dragon_idle1.ogg) that fires on every off→on minecraft theme activation, in lockstep with the existing dragon fly-across GIF. Gated on the same `minecraft-sound` localStorage flag used by the click-sound effect in minecraft-background.tsx so a single mute toggle covers all SFX, and skipped when prefers-reduced-motion is set (matching the visual suppression). Browsers block autoplay before the first user gesture — that's silently caught; the very next theme toggle is itself a gesture and unlocks subsequent plays. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(theme): hover dragon mid-screen so the growl lands on a pause Extends the fly-across to 12s with a 35%→60% (4.2s–7.2s) hold at center, and delays the audio start by 4.2s so the 3s growl sample plays during the held position instead of bleeding across the entrance/exit motion. The wing-flap GIF keeps the dragon visually alive during the hover. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b78a274 commit 635d580

3 files changed

Lines changed: 46 additions & 6 deletions

File tree

26.1 KB
Binary file not shown.

packages/app/src/app/globals.css

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,10 @@
321321

322322
/* Ender-dragon fly-across — one-shot animation on minecraft theme activation,
323323
* triggered from minecraft-decorations.tsx. Plays once (iteration-count: 1)
324-
* and lands the dragon off-screen left so the GIF stops being rendered. */
324+
* and lands the dragon off-screen left so the GIF stops being rendered. The
325+
* dragon hovers in the center between 35% and 60% (4.2s–7.2s of the 12s
326+
* total) so the canonical 3s growl SFX from minecraft-decorations.tsx lines
327+
* up with a deliberate mid-screen pause rather than a moving target. */
325328
@keyframes mc-dragon-flyacross {
326329
0% {
327330
transform: translate(110vw, 0) scale(1);
@@ -330,8 +333,11 @@
330333
8% {
331334
opacity: 0.95;
332335
}
333-
50% {
334-
transform: translate(40vw, -3vh) scale(1.05);
336+
35% {
337+
transform: translate(40vw, -2vh) scale(1.05);
338+
}
339+
60% {
340+
transform: translate(40vw, -2vh) scale(1.05);
335341
}
336342
92% {
337343
opacity: 0.95;
@@ -343,7 +349,7 @@
343349
}
344350

345351
.mc-dragon-flyacross {
346-
animation: mc-dragon-flyacross 9s cubic-bezier(0.4, 0, 0.6, 1) 1 forwards;
352+
animation: mc-dragon-flyacross 12s cubic-bezier(0.4, 0, 0.6, 1) 1 forwards;
347353
will-change: transform, opacity;
348354
}
349355

packages/app/src/components/minecraft/minecraft-decorations.tsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import { useEffect, useState } from 'react';
1212
* All static images are `pointer-events: none` at low z-index. The dragon is
1313
* absolutely positioned and flies right-to-left once per theme activation
1414
* (animation-iteration-count: 1) — the GIF's wing-flap plays during the
15-
* traversal so the entrance feels alive.
15+
* traversal so the entrance feels alive, accompanied by the canonical
16+
* dragon idle/growl SFX (gated on the existing `minecraft-sound` toggle
17+
* and on `prefers-reduced-motion`).
1618
*
17-
* Asset provenance: minecraft.wiki (CC BY-NC-SA 3.0) for blocks/items/dragon.
19+
* Asset provenance: minecraft.wiki (CC BY-NC-SA 3.0) for blocks/items/dragon
20+
* (audio + visuals).
1821
*/
1922
const DECORATIONS = [
2023
// Top-left: Zombified Piglin, mirrored so its sword arm points toward the
@@ -102,6 +105,37 @@ export function MinecraftDecorations() {
102105
return () => observer.disconnect();
103106
}, []);
104107

108+
// Dragon roar SFX — fires on each dragonNonce bump (off→on theme transition,
109+
// and once at mount if the user lands directly on the minecraft theme).
110+
// Gated on `minecraft-sound` (the same toggle that controls click sounds in
111+
// `minecraft-background.tsx`) and on prefers-reduced-motion (skipping the
112+
// sound when the visual fly-across is suppressed). On the first page load
113+
// the browser blocks autoplay until a user gesture exists — silent fail is
114+
// fine; the next theme toggle is itself a gesture and unlocks audio.
115+
//
116+
// The growl is delayed to land on the dragon's mid-screen pause (35% of
117+
// the 12s `mc-dragon-flyacross` keyframes ≈ 4.2s) so the 3s sample plays
118+
// in lockstep with the ~3s hover (35%→60%) rather than over a moving
119+
// target.
120+
useEffect(() => {
121+
if (dragonNonce === 0) return;
122+
if (localStorage.getItem('minecraft-sound') === 'false') return;
123+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
124+
125+
const audio = new Audio('/decorative/minecraft/ender-dragon.mp3');
126+
audio.volume = 0.5;
127+
const timeout = window.setTimeout(() => {
128+
audio.play().catch(() => {
129+
/* browser blocked autoplay — no prior user gesture */
130+
});
131+
}, 4200);
132+
return () => {
133+
window.clearTimeout(timeout);
134+
audio.pause();
135+
audio.src = '';
136+
};
137+
}, [dragonNonce]);
138+
105139
if (!active) return null;
106140

107141
return (

0 commit comments

Comments
 (0)