๐ Familiar TypeScript-like Syntax
Write code in a language you already know. Structs, functions, and control flow just like TypeScript.
struct Player { health: u8; name: string[32]; position: Vec3; } function heal(player: Player, amount: u8) { player.health = min(100, player.health + amount); }
โก Predictable Performance
No garbage collection pauses. No malloc overhead. Know exactly how your memory is used.
// All memory allocated at compile time global gameState: struct { players: Player[100]; // Fixed array particles: Particle[1000]; score: u32; };
๐งช Built-in Testing
Write tests alongside your code. Tests run in development, automatically excluded from production.
test playerTakesDamage(): bool { let p: Player; p.health = 100; takeDamage(p, 30); return p.health == 70; }
๐ง Seamless JavaScript Integration
Automatic TypeScript definitions. Memory inspection tools. Works in browsers and Node.js.
// Auto-generated clean API const game = new GameModule(); await game.loadFromUrl("game.wasm"); game.initPlayer(0, "Alice"); const health = game.players[0].health;
๐ Why Choose Stasis?
โ Easy to Learn
- If you know TypeScript or JavaScript, you already know 90% of Stasis
- No complex memory management or pointer arithmetic
- Clear error messages that guide you to solutions
- Interactive playground to experiment and learn
โ Fast Development
- Write once, run everywhere WASM is supported
- Auto-generated JavaScript/TypeScript bindings
- Built-in test framework for TDD
- Hot reload support for rapid iteration
โ Production Ready
- Predictable performance - no sudden GC pauses
- Tiny binaries - only include code you actually use
- Useful for games and simulations
- Works in browsers and Node.js
- Future: Embedded systems
โ Real-World Use Cases
- Games: Physics engines, game logic, AI
- Audio/Video: DSP, codecs, filters
- Science: Simulations, data processing
- Embedded: IoT devices, microcontrollers
๐ About Stasis
Purpose & Philosophy
Stasis is a statically-allocated, TypeScript-like language that compiles directly to WebAssembly bytecode. Unlike traditional languages that rely on dynamic memory allocation and garbage collection, Stasis takes a radically different approach: all memory is allocated at compile time. This design choice eliminates entire classes of bugs, provides predictable performance, and makes the language perfect for real-time applications, games, and embedded systems.
Key Design Principles
- Zero Dynamic Allocation: No malloc, no GC, no memory fragmentation. All memory layout is determined at compile time.
- Predictable Performance: No GC pauses or allocation overhead. Execution time is deterministic.
- Opcode-Direct Functions: Built-in functions compile to direct WASM opcodes with zero function call overhead.
- Tree-Shaking by Default: Only code that's actually used gets included in the output.
- Built-in Testing: Write tests alongside your code. Tests run in development, automatically excluded from production.
- Type Safety: Complete compile-time type checking with struct layout computation.
Compiler Architecture
The Stasis compiler uses a sophisticated 3-phase architecture optimized for WebAssembly generation:
- Phase 1 - Function Signature Discovery: Fast single-pass scan to collect all function signatures, enabling forward references.
- Phase 2 - Tree-Shaking & Analysis: Starting from exported functions, recursively parse only reachable code.
- Phase 3 - WASM Generation: Generate minimal WASM with optimal section ordering and zero unreferenced code.
Memory Model
Stasis uses a unique memory model where all application state lives in a single global Data structure. Memory offsets and sizes are computed at compile time based on struct definitions. Arrays have fixed sizes, strings are fixed-length UTF-8 buffers, and all memory accesses compile to direct WASM load/store instructions.
๐ง Advanced Features
Opcode-Direct Functions
Functions can compile directly to WASM opcodes for zero overhead:
// Direct WASM opcode - no function call! function add(a: i32, b: i32): i32 { opcode 0x6A; // i32.add } // With immediate values function getConstant(): i32 { opcode 0x41, 42; // i32.const 42 }
Parameterized Structs
Compile-time struct parameters for flexible data structures:
struct Collection(size: i32) { count: i32; maxSize: i32 = size; items: Item[size]; } // Different types at compile time global small: Collection(10); global large: Collection(100);
Array Management
Controlled allocation-like behavior with static guarantees:
let enemies: Enemy[100]; // Check if space available if (enemies.can_add()) { // Get unused slot let enemy = enemies.add(); enemy.health = 100; } // Mark as unused enemies.delete(enemy);
Method-Style Invocation
Natural syntax that compiles identically to function calls:
// These compile identically: player.takeDamage(10); takeDamage(player, 10); // Works with arrays too enemies[5].update(); update(enemies[5]);
๐ฏ Perfect For
๐ฎ Games & Graphics
Predictable frame times, no GC pauses. Perfect for physics engines, renderers, and game logic where every millisecond counts.
๐ต Audio Processing
Real-time DSP, synthesizers, and effects. Static allocation ensures glitch-free audio with consistent latency.
๐ฌ Scientific Computing
Simulations and data processing with deterministic performance. Know exactly how much memory you're using.
๐ค Embedded Systems
Future support for microcontrollers and IoT devices. Fixed memory footprint ideal for constrained environments.
Quick Language Reference
Basic Types
// Numbers let age: u8 = 25; // 0 to 255 let score: i32 = -100; // ยฑ2 billion let distance: f32 = 3.14; // floating point // Text let name: string[32] = "Alice"; // up to 32 chars // Arrays let scores: i32[10]; // 10 integers let matrix: f32[3][3]; // 3x3 matrix
Structs & Functions
// Define a struct struct Enemy { health: u8; pos: f32[2]; // x, y active: bool; } // Function with struct parameter function damageEnemy(e: Enemy, dmg: u8) { if (dmg >= e.health) { e.health = 0; e.active = false; } else { e.health = e.health - dmg; } }
Control Flow
// Conditions if (player.health > 0) { // still alive } else { gameOver(); } // Loops for (let i: i32 = 0; i < 10; i = i + 1) { enemies[i].update(); } // Iterate arrays foreach (enemy in enemies) { if (enemy.active) { renderEnemy(enemy); } }
Global Data & Exports
// All data lives here global gameData: struct { enemies: Enemy[100]; player: Player; score: u32; }; // Export functions for JavaScript export function startGame(): void { initEnemies(); gameData.score = 0; } export function getScore(): u32 { return gameData.score; }