- About Vulfram
- Design Philosophy
- Architecture
- Key Concepts
- Quick Start
- Main Features
- Development
- Project Structure
- Documentation
- Contributing
- License
- About Vulppi
Vulfram is a rendering and systems core written in Rust and exposed as a dynamic library. The name combines "Vulppi" (derived from Vulpes, the scientific name for fox) and "Frame", representing our mission to create perfect frames for incredible interactive experiences.
Vulfram is designed to be host-agnostic and driven by external runtimes via FFI or WASM:
- 🟢 Node.js (N-API)
- 🌙 Lua (via
mlua) - 🐍 Python (via
PyO3) - 🔧 Any environment capable of calling C-ABI functions
- 🌐 Browser runtimes via WASM (WebGPU + DOM canvas)
- 🚀 High Performance: GPU-accelerated rendering with WGPU (WebGPU)
- 🔄 Cross-Platform: Native support for Windows, macOS, and Linux
- 🎮 Complete Input System: Keyboard, mouse, touch, and gamepads (via Gilrs)
- 🪟 Advanced Window Management: Full control over multiple windows (via Winit) or a DOM canvas (WASM)
- 💡 Lighting & Shadows: Support for various light types and shadow mapping
- 🎨 Materials & Textures: Flexible resource management for rendering
- 🔌 Language Bindings: N-API (Node.js), Lua, Python, and more. With C-ABI,
bun:ffiis also possible. - ⚡ MessagePack Communication: Fast binary serialization for commands and events
- 🎯 Host-Agnostic Design: No assumptions about ECS, OOP, or game framework
Vulfram follows a black box approach where:
The host controls the engine only through:
- A small set of C functions (
vulfram_*)- Binary buffers serialized with MessagePack
Design Goals:
- Host-agnostic: Works with any game framework or architecture
- Minimal public surface: Only essential C-ABI functions exposed
- Binary and fast: MessagePack for structured data, raw bytes for heavy assets
- Clear separation: Host manages logic and IDs, Core manages GPU and rendering
- Async linking: Components/resources can be created in any order, using fallbacks until data is available
Vulfram uses a queue-based architecture that enables efficient communication between the host and the Rust core:
┌─────────────────────────────────────┐
│ Host Layer │
│ (JS/TS, Lua, Python, etc.) │
│ • Game Logic │
│ • Entity Management │
│ • ID Generation │
└────────┬────────────┬───────────────┘
│ │
│ Commands │ Events
│ (MsgPack) │ (MsgPack)
▼ ▲
┌────────────────────────────┐
│ vulfram_send_queue() │
│ vulfram_receive_queue() │
│ vulfram_receive_events() │
│ vulfram_upload_buffer() │
│ vulfram_tick() │
└──────────┬─────────────────┘
│
┌──────────────▼───────────────────────┐
│ Vulfram Core (Rust) │
│ • Resource Management │
│ • Component Instances │
│ • Platform Proxy (Desktop/Browser) │
│ • GPU Rendering (WGPU) │
│ • Input Processing (Gilrs/Web) │
└──────────────┬───────────────────────┘
│
┌──────────────▼───────────────────────┐
│ GPU Layer │
│ Vulkan / Metal / DirectX 12 │
└──────────────────────────────────────┘
Host (Your Game/App):
- Manage game logic and world state
- Generate logical IDs (entities, resources, buffers)
- Build MessagePack command batches
- Drive the main loop with
vulfram_tick() - Process events and responses
Vulfram Core:
- Abstract window, input, and rendering systems via platform proxies
- Manage GPU resources and pipelines using WGPU
- Track component instances (cameras, models, etc.)
- Translate commands into internal state changes
- Render frames efficiently
Vulfram distinguishes between two fundamental types:
Components - High-level structures describing scene participation:
- Always attached to a host-chosen ID (e.g.
camera_id,model_id,light_id) - Examples:
Camera,Model,Light - Can contain static data (local colors, matrices)
- Reference sharable resources by logical ID
- Created/updated via MessagePack commands
Resources - Reusable assets used by components:
- Identified by logical IDs:
GeometryId,MaterialId,TextureId - Sharable across multiple components/entities
- Have internal GPU handles (buffers, textures, pipelines)
- Examples: geometries, textures, materials
The host manages all logical identifiers:
WindowId- Window instancesCameraId- Camera instancesModelId- Model instancesLightId- Light instancesGeometryId- Mesh/geometry assetsMaterialId- Material configurationsTextureId- Texture assetsBufferId- Upload blob identifiers
These are opaque integers to the core. The host ensures uniqueness and consistency.
Vulfram allows resources to be created out of order:
- Models can reference geometry or material IDs that do not exist yet.
- Materials can reference texture IDs that do not exist yet.
In these cases, Vulfram renders using fallback resources until the real resources are created later with the same IDs. When a referenced resource appears, it is picked up automatically on the next frame without recreating the model or material.
This enables async streaming and decouples creation order.
Vulfram uses u32 bitmasks for visibility control:
layerMaskCamera- Specifies which layers a camera can seelayerMaskComponent- Specifies which layers a model belongs tolayerMaskLight- (Future) Which layers a light affects
Visibility Rule:
Visible if: (layerMaskCamera & layerMaskComponent) > 0
This enables:
- World-only or UI-only cameras
- Team-based rendering
- Debug geometry filtering
- Multi-pass rendering strategies
- A single geometry can be referenced by many models.
- A single material can be referenced by many models.
- A single texture can be referenced by many materials.
There is no ownership tracking. If a resource is disposed while still referenced, rendering falls back gracefully.
Per camera:
- Opaque/masked objects are sorted by
material_idthengeometry_idto reduce state changes and batch draw calls. - Transparent objects are sorted by depth for correct blending.
Draw calls are batched by runs of (material_id, geometry_id) after sorting.
Heavy data uses one-shot uploads:
- Host calls
vulframUploadBuffer(bufferId, type, data) - Core stores blob in internal upload table
Create*commands referencebufferIdto consume data- Entry is marked as used and can be removed
CmdUploadBufferDiscardAllcommand clears all pending uploads
Uploads are independent of model/material creation, so you can create components first and upload data later.
- Rust 1.70+ (rustup.rs)
- Vulkan, Metal, or DirectX 12 updated drivers
# Clone the repository
git clone https://github.com/vulppi-dev/vulfram.git
cd vulfram
# Build and run the test harness
cargo runThe test harness lives in src/main.rs and exercises:
- window creation
- primitive geometry creation
- camera + model setup
- basic rendering loop
- Multiple window creation and management
- Window state control (normal, minimized, maximized, fullscreen)
- Position and size configuration
- Borderless and resizable options
- Custom window icons and cursors
- Drag-and-drop file support
- Window events (resize, move, focus, close)
- Keyboard: Physical key events with modifiers and IME support
- Mouse: Movement, buttons, scroll wheel
- Touch: Multi-touch support with gestures (pinch, pan, rotate) on native
- Pointer: Unified API for mouse/touch/pen via
PointerEvent - Gamepad: Automatic detection, buttons, analog sticks, triggers (Gilrs on native, Gamepad API on web)
- Standard mapping with dead zones
- Change threshold filtering for efficient event generation
- GPU-accelerated rendering via WebGPU
- Cross-platform support (Vulkan, Metal, DirectX 12)
- Buffer upload for textures and meshes
- Efficient CPU-GPU synchronization
- Component-based rendering system
- Layer-based visibility control
- MessagePack serialization for fast binary communication
- Separate queues for:
- Commands (host → core)
- Responses (core → host)
- Events (core → host)
- Profiling data export for performance analysis
- Event caching: Filters duplicate events to reduce overhead
- Redraw optimization: Dirty flag system for selective rendering
- Dead zone filtering: Reduces gamepad noise
- Batch processing: Commands processed in bulk for efficiency
# Build Rust core
cargo build --release
# Run tests
cargo test
# Check code with Clippy
cargo clippy
# Format code
cargo fmt1. Update host-side logic (game state, entities)
2. Upload heavy data (optional) via `vulfram_upload_buffer()`
3. Send command batch via `vulfram_send_queue()`
4. Advance the core via `vulfram_tick(time, delta_time)`
5. Receive responses via `vulfram_receive_queue()`
6. Receive events via `vulfram_receive_events()`
7. Read profiling data (optional) via `vulfram_get_profiling()`
vulfram_receive_queue() consumes and clears the internal response queue.
vulfram/
├── src/ # Rust core
│ ├── lib.rs # Crate entry point (cdylib)
│ └── core/ # Engine core modules
│ ├── cmd.rs # Command routing
│ ├── lifecycle.rs # Init/dispose lifecycle
│ ├── queue.rs # Command/response/event queues
│ ├── singleton.rs # Global state management
│ ├── state.rs # Engine state
│ ├── tick.rs # Frame update logic
│ ├── platforms/ # Platform proxies
│ │ ├── desktop/ # Winit + native input
│ │ └── browser/ # DOM canvas + web input
│ ├── window/ # Window commands/events/state
│ ├── input/ # Keyboard/pointer events + cache
│ ├── gamepad/ # Gamepad events + cache
│ ├── render/ # Rendering system
│ └── resources/ # Camera/model/light/material/texture
│
├── docs/ # Documentation
├── assets/ # Visual resources
├── Cargo.toml # Rust dependencies
└── README.md # This file
Comprehensive documentation is available in the docs/ folder.
Not sure where to start? Check our complete documentation index for guided navigation based on your role and needs.
For Engine Users (Binding Authors):
- OVERVIEW.md - Start here! High-level concepts and design philosophy
- ABI.md - C-ABI functions, usage contract, and calling conventions
- ARCH.md - Architecture, lifecycle, and main loop patterns
- GLOSSARY.md - Terminology and naming conventions
For Core Contributors (Rust Developers):
- OVERVIEW.md - Core concepts and design
- ARCH.md - System architecture
- API.md - Internal Rust API, crates, and data structures
- GLOSSARY.md - Internal terminology
Additional Resources:
- UI.md - UI runtime technical index
- ui/README.md - RealmUI overview and subsystem docs
- PLATFORM-PROXIES.md - Platform proxy architecture
- VALIDATION.md - Automated tests and manual demo validation scope
- Copilot Instructions - Development patterns and memory
Automated tests do not fully cover perceptual/platform-dependent flows (window lifecycle details, audio audibility, visual quality). Those are validated manually by running demos as defined in docs/VALIDATION.md.
Contributions are welcome! Please follow these guidelines:
- Rust code: Minimal comments, self-descriptive names
- All code: English for variables, functions, types, and comments
- Documentation: English for all docs (README, API docs)
- Communication: Brazilian Portuguese for discussions and issues
- Fork the project
- Create a feature branch (
git checkout -b feature/MyFeature) - Follow the project's coding conventions (see
.github/copilot-instructions.md) - Write tests for your changes
- Ensure all tests pass (
cargo test) - Format your code (
cargo fmt) - Check for issues (
cargo clippy) - Commit your changes (
git commit -m 'Add MyFeature') - Push to your branch (
git push origin feature/MyFeature) - Open a Pull Request
- Keep the C-ABI surface minimal
- Use MessagePack for all structured data crossing the ABI
- Maintain clear separation between host and core responsibilities
- Follow the component/resource model
- Ensure thread safety (all functions are main-thread only)
wgpu- GPU abstraction layer (WebGPU implementation)winit- Cross-platform windowinggilrs- Gamepad inputnapi- Node.js N-API bindings (optional)serde- Serialization frameworkrmp-serde- MessagePack serializationglam- Vector and matrix mathbytemuck- Safe type conversions for GPU dataimage- Image loading and decoding
Bindings are provided via feature flags (napi, lua, python) and are
implemented in Rust.
This project is licensed under the MIT License. See the LICENSE.md file for details.
Vulppi is a company focused on creating cutting-edge technologies for game development and interactive applications. Our name comes from Vulpes, the scientific name for fox, symbolizing agility, intelligence, and adaptability.