@bloomengine/engine
v0.4.16
Published
Bloom Engine: native TypeScript game engine compiled by Perry
Downloads
324
Maintainers
Readme
Bloom Engine
Native games from TypeScript.
Write TypeScript. Ship native games — and now the web too. Bloom compiles your game to Metal, DirectX 12, Vulkan, OpenGL, and WebGPU — one codebase for every platform.
Inspired by raylib. Bloom models its public API on raylib's — in our view one of the best API designs in gamedev. Bloom is an independent implementation, not a port — how Bloom relates to raylib »
Install
npm install @bloomengine/engineOr with your preferred package manager:
bun add @bloomengine/engine
pnpm add @bloomengine/engine
yarn add @bloomengine/engineThe npm package ships the TypeScript API alongside the engine's Rust sources and the bundled JoltPhysics C++ shim, so a single install is enough — there's no separate native download step.
You'll also need:
- Perry — the TypeScript AOT compiler that turns your game into a native binary or WASM module. It also drives the engine's native build.
- Rust toolchain (rustup.rs) — Perry invokes Cargo to compile the engine's platform crate the first time you build for each target.
- For web builds only: wasm-pack (
cargo install wasm-pack).
Quick Start
import { initWindow, windowShouldClose, beginDrawing,
endDrawing, clearBackground, drawText, Colors } from "@bloomengine/engine";
initWindow(800, 450, "My Game");
while (!windowShouldClose()) {
beginDrawing();
clearBackground(Colors.SNOW);
drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
endDrawing();
}Web-Compatible Pattern
Use runGame() for code that works on both native and web:
import { initWindow, runGame, clearBackground, drawText, Colors } from "@bloomengine/engine";
initWindow(800, 450, "My Game");
runGame((dt) => {
clearBackground(Colors.SNOW);
drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
});Build for web:
./native/web/build.sh main.ts
cd dist/web && python3 -m http.server 8080Features
- Simple API — Functions, not classes. The entire API fits on a cheatsheet. (design rationale)
- True native — Compiles to Metal, DirectX 12, Vulkan, OpenGL, and WebGPU via wgpu.
- Ship everywhere — macOS, Windows, Linux, iOS, tvOS, Android, and Web from one codebase.
- Unified 2D/3D — Shapes, textures, text, 3D models, and audio in one engine.
- Zero magic — Explicit game loops, no hidden framework overhead.
How Bloom relates to raylib
Bloom's public API is heavily inspired by raylib.
raylib's API is, in our opinion, one of the best in the gamedev space — a flat library
of plain functions, no classes, small enough to learn from a cheatsheet — so we model
ours on it. You'll recognize the shape immediately: initWindow, beginDrawing,
clearBackground, drawText, and modules named core / shapes / textures / text /
audio / models.
That's where the relationship ends. Bloom's implementation is entirely independent — it does not link against, embed, or call raylib. Bloom compiles TypeScript directly to native code via Perry, our LLVM-based AOT compiler, and renders through wgpu (Metal, DirectX 12, Vulkan, OpenGL, WebGPU). It is not a port or a binding — just an engine that admires raylib's API design. Thanks to Ramon Santamaria (@raysan5) and the raylib community for setting the bar. (full design rationale)
Modules
| Module | Import | Description |
|--------|--------|-------------|
| Core | @bloomengine/engine/core | Window, game loop, input, timing |
| Shapes | @bloomengine/engine/shapes | 2D drawing + collision detection |
| Textures | @bloomengine/engine/textures | Image loading, sprite batching |
| Text | @bloomengine/engine/text | TTF/OTF font loading and rendering |
| Audio | @bloomengine/engine/audio | Sound effects + music streaming |
| Models | @bloomengine/engine/models | 3D model loading (glTF, OBJ), skeletal animation |
| Math | @bloomengine/engine/math | Vectors, matrices, quaternions, easing |
| Physics | @bloomengine/engine/physics | Jolt-backed rigid + soft bodies, character, vehicles (docs) |
Platforms
| Platform | Graphics API | Input | |----------|-------------|-------| | macOS | Metal | Keyboard + mouse | | Windows | DirectX 12 | Keyboard + mouse | | Linux | Vulkan / OpenGL | Keyboard + mouse | | iOS | Metal | Touch + gamepad | | tvOS | Metal | Siri Remote + gamepad | | Android | Vulkan / OpenGL ES | Touch + gamepad | | Web | WebGPU / WebGL | Keyboard + mouse + touch + gamepad |
Architecture
src/ TypeScript API
core/ Window, input, game loop
shapes/ 2D shapes + collision
textures/ Image loading, sprites
text/ Font rendering
audio/ Sound + music
models/ 3D models
math/ Vectors, matrices, easing
native/ Rust implementations
shared/ Cross-platform core (wgpu, fontdue, gltf)
macos/ Metal + AppKit + Core Audio
ios/ Metal + UIKit + Core Audio
tvos/ Metal + UIKit + GCController
windows/ DirectX 12 + Win32 + XAudio2
linux/ Vulkan/OpenGL + X11/Wayland + PulseAudio
android/ Vulkan/OpenGL ES + NativeActivity + AAudio
web/ WebGPU/WebGL + Canvas + Web Audio (WASM)
examples/
pong/ Complete working example (~170 lines)Types
Plain interfaces, no classes:
interface Vec2 { x: number; y: number }
interface Vec3 { x: number; y: number; z: number }
interface Color { r: number; g: number; b: number; a: number }
interface Rect { x: number; y: number; width: number; height: number }
interface Camera2D { offset: Vec2; target: Vec2; rotation: number; zoom: number }
interface Camera3D { position: Vec3; target: Vec3; up: Vec3; fovy: number; projection: number }
interface Texture { handle: number; width: number; height: number }
interface Sound { handle: number }
interface Model { handle: number }Fullscreen
Launch your game in fullscreen by passing true as the fourth argument to initWindow:
initWindow(800, 450, "My Game", true); // launches fullscreen
initWindow(800, 450, "My Game"); // windowed (default)Toggle fullscreen at runtime:
if (isKeyPressed(Key.F11)) {
toggleFullscreen();
}Fullscreen is supported on macOS (native AppKit fullscreen), Windows (borderless fullscreen), and Linux (EWMH/X11). The width and height you pass are used as the windowed dimensions when exiting fullscreen.
Skeletal Animation
Bloom supports GPU-accelerated skeletal animation via glTF/GLB models. The pipeline uses 4-bone linear blend skinning with a 128-joint uniform buffer, running entirely on the GPU.
import { loadModel, loadModelAnimation, updateModelAnimation, drawModel,
getTime, Colors } from "@bloomengine/engine";
const character = loadModel("assets/models/character.glb");
const anim = loadModelAnimation("assets/models/character.glb");
// In your game loop:
updateModelAnimation(anim, 0, getTime(), 1.0, 0, 0, 0);
drawModel(character, { x: 0, y: 0, z: 0 }, 1.0, Colors.WHITE);Key functions:
loadModel(path)-- loads GLB with skin data (JOINTS_0, WEIGHTS_0)loadModelAnimation(path)-- loads skeleton + animation channels from GLBupdateModelAnimation(handle, animIndex, time, scale, px, py, pz)-- samples animation, computes joint matricesdrawModel(model, position, scale, tint)-- renders with GPU skinning
For the full pipeline (Blender export, pitfalls, architecture), see docs/skeletal-animation.md.
