diff --git a/apps/code/src/renderer/components/HedgehogMode.tsx b/apps/code/src/renderer/components/HedgehogMode.tsx index cc3d795848..ebf0467869 100644 --- a/apps/code/src/renderer/components/HedgehogMode.tsx +++ b/apps/code/src/renderer/components/HedgehogMode.tsx @@ -5,10 +5,15 @@ import type { HedgeHogMode as HedgehogModeGame, } from "@posthog/hedgehog-mode"; import { logger } from "@utils/logger"; +import { playSoundUrl, WILHELM_SOUND_URL } from "@utils/sounds"; import { useEffect, useRef } from "react"; const log = logger.scope("hedgehog-mode"); +// Above the autonomous jump velocity of 15, so jumps never trigger. +const HARSH_THROW_Y_THRESHOLD = 25; +const HARSH_THROW_SPEED_THRESHOLD = 25; + export function HedgehogMode() { const hedgehogMode = useSettingsStore((s) => s.hedgehogMode); const setHedgehogMode = useSettingsStore((s) => s.setHedgehogMode); @@ -30,6 +35,27 @@ export function HedgehogMode() { | HedgehogActorOptions | undefined; + const onPointerUp = () => { + // Defer one frame so Matter.js applies the post-release velocity. + requestAnimationFrame(() => { + if (cancelled || !gameRef.current) return; + for (const hedgehog of gameRef.current.getAllHedgehogs()) { + const v = hedgehog.rigidBody?.velocity; + if (!v) continue; + const speed = Math.hypot(v.x, v.y); + if ( + v.y < -HARSH_THROW_Y_THRESHOLD && + speed > HARSH_THROW_SPEED_THRESHOLD + ) { + const volume = useSettingsStore.getState().completionVolume; + playSoundUrl(WILHELM_SOUND_URL, volume); + break; + } + } + }); + }; + window.addEventListener("pointerup", onPointerUp); + import("@posthog/hedgehog-mode") .then(async ({ HedgeHogMode }) => { if (cancelled) return; @@ -62,6 +88,7 @@ export function HedgehogMode() { return () => { cancelled = true; + window.removeEventListener("pointerup", onPointerUp); }; }, [hedgehogMode, user?.hedgehog_config, setHedgehogMode]); diff --git a/apps/code/src/renderer/utils/sounds.ts b/apps/code/src/renderer/utils/sounds.ts index b0abc7b0f3..ef9359196e 100644 --- a/apps/code/src/renderer/utils/sounds.ts +++ b/apps/code/src/renderer/utils/sounds.ts @@ -13,6 +13,16 @@ import slideUrl from "@renderer/assets/sounds/slide.mp3"; import switchUrl from "@renderer/assets/sounds/switch.mp3"; import wilhelmUrl from "@renderer/assets/sounds/wilhelm.mp3"; +export const WILHELM_SOUND_URL = wilhelmUrl; + +export function playSoundUrl(url: string, volume = 80): void { + const audio = new Audio(url); + audio.volume = Math.max(0, Math.min(100, volume)) / 100; + audio.play().catch(() => { + // Audio play can fail if user hasn't interacted with the page yet + }); +} + const SOUND_URLS: Record, string> = { guitar: guitarUrl, danilo: daniloUrl,