GameWindow component provides the native fullscreen game experience with emulation controls, save states, shaders, and power-on/off animations.
Import
GameWindow is a desktop-specific component located in apps/desktop/src/renderer/components/. It manages WebGL rendering, audio, input, and native window controls.Basic usage
GameWindow is automatically loaded by the main process when a game is launched. It receives game data via IPC events, so no props are needed.
Features
Emulation controls
- Play/Pause - Space bar or button
- Reset - Restart the game
- Screenshot - Capture current frame
- Fast forward - Tab key toggles 1x ↔ 2x (or custom speed)
- Speed menu - Dropdown with 1x, 1.5x, 2x, 3x, 4x, 8x options
Save states
- 5 save slots (0-4) with visual slot selector
- Save - F5 key or button
- Load - F9 key or button
- Saves are stored per-game in
~/Library/Application Support/GameLord/saves/
Shaders
Available shader presets:- Default - Pixelated, no shader
- CRT - Scanlines and curvature
- LCD - Handheld LCD effect
- Smooth - Bilinear filtering
Audio controls
- Volume slider (0-100%)
- Mute toggle button
- Volume preference persisted to localStorage
Power animations
System-specific boot/shutdown animations:- CRT (NES, SNES, Genesis) - Scanline collapse, white flash
- LCD Handheld (Game Boy, Game Boy Color) - Slow vertical fade
- LCD Portable (GBA, DS) - Fast fade with pixel shimmer
Keyboard controls
Game input
| Key | Button |
|---|---|
| Z | A |
| X | B |
| A | X |
| S | Y |
| Arrow keys | D-pad |
| Enter | Start |
| Shift | Select |
| Q | L |
| W | R |
Emulator shortcuts
| Key | Action |
|---|---|
| Space | Pause/Resume |
| Tab | Fast forward toggle |
| F5 | Save state |
| F9 | Load state |
Gamepad support
GameWindow automatically detects and polls connected gamepads:- Standard gamepad mapping (Xbox, PlayStation, Switch Pro)
- Up to 4 controllers supported
- Green gamepad icon shown when controller connected
- Polling disabled when paused to save CPU
IPC events
GameWindow listens to these IPC events from the main process:Emitted when a game is loaded. Sets the window title and loads system shader preference.
Emitted after hero transition completes. Starts the power-on animation and enables rendering.
Emitted when the core reports AV info. Updates canvas dimensions and aspect ratio.
Emitted for each video frame (fallback IPC path). Buffered and rendered in rAF loop.
Emitted for audio chunks (fallback IPC path). Scheduled via Web Audio API.
Emitted when the user closes the window. Starts power-off animation and hides controls.
Emitted when emulation pauses. Updates pause button state.
Emitted when emulation resumes. Updates pause button state.
Emitted when emulation speed changes. Updates speed button label.
SharedArrayBuffer mode
GameWindow supports zero-copy video/audio via SharedArrayBuffer:- Main process sends SABs via MessagePort
- Renderer gets typed array views (control, video, audio)
- Video frames read directly from SAB in rAF loop
- Audio drained from ring buffer on each frame
- No IPC events needed during gameplay
SAB mode reduces video frame latency from ~8ms to ~2ms and eliminates IPC overhead during gameplay.
WebGL rendering
GameWindow uses theWebGLRenderer class from @gamelord/ui:
Rendering pipeline
- IPC buffer - Video frame arrives and is buffered
- rAF callback - Triggered on next vsync
- WebGL draw - Frame uploaded to texture and rendered
- Shader pass - Fragment shader applies effects
- Present - SwapBuffers presents to screen
Control bar behavior
Auto-hide logic
- Controls slide in on first genuine cursor movement
- Auto-hide after 1 second of inactivity
- Stay visible while dropdown menus are open
- Stay visible while cursor is over control bar
- Hide immediately when cursor leaves window
Drag region
- Persistent drag region at top edge (always draggable)
- Top control bar uses
WebkitAppRegion: drag - Buttons opt out with
WebkitAppRegion: no-drag
Display type detection
Power animations are selected based on system ID:Performance considerations
Frame pacing
- WebGL rendering synced to
requestAnimationFrame - FPS measured via exponential moving average
- FPS counter updated every 30 frames (~500ms)
Audio scheduling
- Pre-buffer 60ms to prevent crackling
- Max lookahead 120ms to prevent drift
- Reset buffer if audio gets too far ahead
Input polling
- Keyboard events handled directly (low latency)
- Gamepad polled in rAF loop (60fps)
- Polling disabled when paused
Settings menu
Development-only settings (shown whenNODE_ENV === 'development'):
- Show FPS - Displays frame rate overlay
- Annotate - Enables Agentation annotation toolbar
Traffic light visibility
Native macOS traffic lights (close/minimize/maximize) are:- Hidden during power-on animation
- Shown when controls are visible
- Hidden during power-off animation
- Hidden when controls auto-hide