Skip to content

React Hooks

Access the game context (snapshot and actions) from any component inside a GameProvider.

import { useGame } from '@doodle-engine/react';
function MyComponent() {
const { snapshot, actions } = useGame();
return (
<div>
<h1>{snapshot.location.name}</h1>
<button onClick={() => actions.talkTo('bartender')}>
Talk to {snapshot.charactersHere[0]?.name}
</button>
</div>
);
}
interface GameContextValue {
snapshot: Snapshot;
actions: {
selectChoice: (choiceId: string) => void;
talkTo: (characterId: string) => void;
takeItem: (itemId: string) => void;
travelTo: (locationId: string) => void;
writeNote: (title: string, text: string) => void;
deleteNote: (noteId: string) => void;
setLocale: (locale: string) => void;
saveGame: () => SaveData;
loadGame: (saveData: SaveData) => void;
};
}

Must be used inside a GameProvider. Throws an error if used outside.

Each action calls the corresponding engine method and updates the snapshot:

ActionDescription
selectChoice(choiceId)Pick a dialogue choice
talkTo(characterId)Start conversation with a character
takeItem(itemId)Pick up an item at current location
travelTo(locationId)Travel to a map location
writeNote(title, text)Add a player note
deleteNote(noteId)Remove a player note
setLocale(locale)Change language
saveGame()Returns SaveData (doesn’t update snapshot)
loadGame(saveData)Restores state and updates snapshot

Manages audio playback automatically based on snapshot changes.

import { useAudioManager } from '@doodle-engine/react';
function MyGame() {
const { snapshot } = useGame();
const controls = useAudioManager(snapshot, {
audioBasePath: '/audio',
masterVolume: 1.0,
musicVolume: 0.7,
soundVolume: 0.8,
voiceVolume: 1.0,
crossfadeDuration: 1000,
});
return (
<div>
<button onClick={() => controls.setMasterVolume(0.5)}>
Half Volume
</button>
<button onClick={controls.stopAll}>Mute</button>
</div>
);
}
ParameterTypeDescription
snapshotSnapshotCurrent game snapshot
optionsAudioManagerOptionsOptional configuration
OptionTypeDefaultDescription
audioBasePathstring'/audio'Base path for audio files
masterVolumenumber1.0Master volume multiplier (0-1)
musicVolumenumber0.7Music channel volume (0-1)
soundVolumenumber0.8Sound effects volume (0-1)
voiceVolumenumber1.0Voice channel volume (0-1)
crossfadeDurationnumber1000Music crossfade duration in ms
interface AudioManagerControls {
setMasterVolume: (volume: number) => void;
setMusicVolume: (volume: number) => void;
setSoundVolume: (volume: number) => void;
setVoiceVolume: (volume: number) => void;
stopAll: () => void;
}

The hook manages three channels:

ChannelSourceBehavior
Musicsnapshot.musicLoops, crossfades between tracks
Voicesnapshot.dialogue?.voicePlays dialogue voice lines
Soundsnapshot.pendingSoundsOne-shot effects, cleared after playing
  • When snapshot.music changes, the current track crossfades to the new one
  • When snapshot.dialogue?.voice is present, the voice file plays
  • All entries in snapshot.pendingSounds are played as one-shot effects
  • Volume levels are applied as channelVolume × masterVolume

Standalone hook for UI chrome sounds (clicks, menu open/close). This is separate from useAudioManager because it handles renderer UI sounds, not game content audio.

import { useUISounds } from '@doodle-engine/react';
function MyUI() {
const uiSounds = useUISounds({
basePath: '/audio/ui',
volume: 0.5,
sounds: {
click: 'click.ogg',
menuOpen: 'menu_open.ogg',
menuClose: 'menu_close.ogg',
},
});
return (
<button
onClick={() => {
uiSounds.playClick();
doSomething();
}}
>
Click Me
</button>
);
}
OptionTypeDefaultDescription
enabledbooleantrueEnable/disable UI sounds
basePathstring'/audio/ui'Base path for UI sound files
volumenumber0.5Volume level (0-1)
soundsobjectCustom sound file names
sounds.clickstring'click.ogg'Click sound file
sounds.menuOpenstring'menu_open.ogg'Menu open sound file
sounds.menuClosestring'menu_close.ogg'Menu close sound file
interface UISoundControls {
playClick: () => void;
playMenuOpen: () => void;
playMenuClose: () => void;
playSound: (key: string) => void;
setEnabled: (enabled: boolean) => void;
setVolume: (volume: number) => void;
enabled: boolean;
volume: number;
}

GameShell uses useUISounds internally. Configure via the uiSounds prop:

<GameShell
registry={registry}
config={config}
uiSounds={{
basePath: '/audio/ui',
volume: 0.5,
sounds: { click: 'click.ogg' },
}}
/>

Pass uiSounds={false} to disable UI sounds entirely.