Overview
The web version runs entirely in the browser using WebAssembly (WASM). The Rust core compiles toemu_core.wasm (~96KB gzipped) and provides a JavaScript interface via wasm-bindgen.
Architecture
Prerequisites
Building
Development Mode (with Hot Reload)
http://localhost:5173 with hot module replacement.
Production Build
The production build is optimized with
wasm-opt and gzips to ~96KB.Building with CEmu Backend
cemu.wasm module.
Integration Guide
Step 1: Create Backend Instance
TheRustBackend class wraps the WASM module:
RustBackend.ts
Step 2: Load ROM and Start Emulation
Loading ROM
Step 3: Run the Emulation Loop
UserequestAnimationFrame for smooth rendering:
Emulation Loop
Step 4: Render the Display
Draw the framebuffer to a canvas:Rendering
Step 5: Handle Keyboard Input
Map keyboard keys to the TI-84 Plus CE key matrix:Key Input
Full Keyboard Controls
Full Keyboard Controls
From
source/web/src/Calculator.tsx:26:| Key | Function |
|---|---|
| 0-9 | Number keys |
| + - * / | Math operations |
| ( ) | Parentheses |
| ^ | Power |
| . | Decimal |
| , | Comma |
| _ | Negate (-) |
| Enter | Enter |
| Backspace/Delete | Del |
| Arrow keys | Navigation |
| Escape | Clear |
| Shift | 2nd |
| Alt | Alpha |
| O | ON |
| Space | Pause/Resume |
| V | √ (square root) |
| S/C/T | Sin/Cos/Tan |
| L/G | Ln/Log |
| M | Math |
| R | x⁻¹ |
| X | X,T,θ,n |
| Insert | Sto |
| F1-F5 | Y=/Window/Zoom/Trace/Graph |
| Home | Apps |
| P | Prgm |
| PageDown | Prgm |
| PageUp | Vars |
| End | Stat |
Step 6: State Persistence
Save and restore emulator state using IndexedDB:State Management
Step 7: Loading Program Files
Inject.8xp (programs) or .8xv (app variables) into the calculator’s flash:
Program Loading
Drag and Drop Support
Implement drag-and-drop for ROM and program files:Drag and Drop
Backend Switching
Switch between Rust and CEmu backends at runtime:Backend Switching
WASM API
The WASM module is generated by wasm-bindgen and provides a JavaScript interface:WasmEmu Class
emu_core.d.ts
Initialization
WASM Init
Performance Optimization
Cycle Budget
Adjust the cycle count based on your performance needs:Performance Tuning
Frame Skipping
Skip rendering during fast emulation:Frame Skipping
WASM Memory Management
The WASM module uses a linear memory buffer that can grow:Memory Management
Example App
The reference implementation is available atsource/web/:
- Calculator.tsx - Main React component with emulation loop
- RustBackend.ts - WASM backend wrapper
- StateStorage.ts - IndexedDB persistence
- Keypad.tsx - On-screen keypad component
API Reference
RustBackend Class
Initialize the WASM module. Must be called before any other operations.
Destroy the emulator instance and free WASM resources.
Load ROM data into the emulator.Parameters:
data: ROM file contents (typically 4MB for TI-84 Plus CE)
0 on success, negative error code on failureInject a
.8xp or .8xv file into flash (cold boot). Must be called after loadRom() and before powerOn().Parameters:data: Program or AppVar file contents
Inject a file into a running emulator (hot reload). Performs a soft reboot.Parameters:
data: Program or AppVar file contents
Simulate ON key press+release to start execution. Call after
loadRom().Reset the emulator to initial state (cold boot).
Run emulation for the specified number of CPU cycles.Parameters:
cycles: Number of cycles to execute
Run one frame of emulation (800,000 cycles at 48MHz/60FPS).
Get the framebuffer width (320).
Get the framebuffer height (240).
Get the framebuffer as RGBA8888 pixels (320×240×4 = 307,200 bytes).Returns: Uint8Array with RGBA pixel data
Set key state in the 8×7 key matrix.Parameters:
row: Key row (0-7)col: Key column (0-7)down:trueif pressed,falseif released
Check if the LCD is on (should display content).Returns:
true if LCD is active, false if off or sleepingSave the current emulator state (includes WASM memory snapshot).Returns: State data, or null on failure
Load a saved emulator state.Parameters:
data: Previously saved state data
true on success, false on failureTroubleshooting
WASM module fails to load
WASM module fails to load
Ensure the WASM file is being served with the correct MIME type:For Vite, this is handled automatically. For other servers, configure MIME types:
nginx.conf
Memory grows unbounded
Memory grows unbounded
Call
destroy() when unmounting the component to free WASM resources:Emulator runs slowly
Emulator runs slowly
- Check browser console for slow frame warnings
- Reduce cycle count: try 400,000 cycles per frame
- Enable WASM SIMD in your browser flags (Chrome/Edge)
- Profile with Chrome DevTools Performance tab
State persistence fails
State persistence fails
Check that IndexedDB is available:Clear stale data:
Screen stays black after loading ROM
Screen stays black after loading ROM
Call
powerOn() after loading the ROM:Next Steps
Android Integration
Learn how to integrate the emulator into Android apps with Kotlin
iOS Integration
Build an iOS version with Swift and SwiftUI