Hewn
Hewn is a minimal Rust game engine for learning and tinkering, with support for terminal, desktop, and web platforms.
- Crate: crates.io/hewn
- Examples:
examples/asciijump,examples/asciibird,examples/snake
Features
[!WARNING] Hewn has only been tested on macOS so far. Windows and Linux support is untested and may have issues.
- 🖥️ Terminal - ASCII games in your terminal with debug output
- 🖼️ Desktop - Hardware-accelerated graphics with WGPU
- 🌐 Web - Deploy to HTML5 canvas
- 🎮 ECS - Entity Component System architecture
- ⚡ Cross-Platform - Write once, run anywhere
Getting started
[!NOTE] Complete tutorial code is available in
examples/tutorial/. The following tutorial builds up from the simplest possible game.
Step 1: Hello World
Let's start with the simplest possible game - showing debug text in the terminal.
First, let's create the basic game structure:
use ;
use Duration; // NEW: dt for frame time
- Import
GameHandlertrait - the core interface all Hewn games implement - Import
ECS- the Entity Component System that manages game objects HelloGamestruct holds our game state (just an ECS for now)- Implement
GameHandlertrait with required methods debug_str()returns text that appears at the bottom of the terminal
Next, let's run our game:
use TerminalRuntime;
// ..
- Create an instance of our game
- Create a terminal runtime with 20×20 character display. We will get to the window runtime later.
- Start the game loop - this runs until the user presses 'Q'
This creates a minimal game that shows "Hello Hewn! Press Q to exit." at the bottom of your terminal. All Hewn games implement the GameHandler trait and need an ECS (Entity Component System) to manage game objects.
[!TIP] Run this with
cargo runand you'll see a field of.characters representing empty space, with your debug text at the bottom. We're about to add a character that moves around this world!
[!NOTE] Delta time:
next(dt: Duration)provides the time since the last frame. Treat velocities as "world units per second" — the ECS scales movement and collision bydtso motion is frame-rate independent.
Step 2: Add a Visible Character
Now let's add a character that appears on screen. This is where the ECS comes in - we'll create an entity with position and rendering components.
First, let's create the player entity:
// ..
use ;
// ..
- Added
player_idfield to store a reference to our character entity - Player positioned at coordinates (5, 5) in the game world
RenderComponentmakes the entity appear as@character on screen - we also include anrgbwith the colour for wgpu rendering.SizeComponentdefines the entity's collision box (1×1 unit)
Next, let's update the game loop and debug display:
// ..
// ..
ecs.step()updates all entities each frame (position, rendering, etc.)- Look up the player entity by its ID to access its components
- Debug text now shows the player's live position coordinates
Now you'll see an @ character in your terminal surrounded by . tiles! The debug text at the bottom shows its exact position.
Step 3: Add Movement
Let's make our character respond to arrow keys using a controller pattern.
First, let's create a controller to track key states:
[!NOTE] Coordinate system:
xincreases to the right andyincreases upward. For example, pressing Up sets a positiveyvelocity.
// ..
use ;
use Duration; // NEW!
// Add a controller to track key states
// ..
GameControllerstruct tracks the current state of arrow keys (pressed/not pressed)handle_key()updates the key states when keys are pressed or released
Next, let's integrate the controller into our game and add velocity:
// ..
// ..
- Added
game_controllerfield to track input state - Player now has a
VelocityComponent- the ECS automatically moves entities with velocity - Player size is
2x1so it appears wider in the terminal - Initialize the controller in the constructor
next()method reads controller state and updates player velocity accordinglyecs.step()applies the velocity to move the playerhandle_key()delegates to the controller for clean separation of concerns
Your @ character now responds to arrow keys! Try moving around and watch the debug text update with your position. Now let's see the same game running in a desktop window...
Step 4: Add Collision Detection
Let's add a wall that blocks the player's movement to make it feel like a real game.
First, let's add a wall entity:
// ..
// ..
- Wall positioned at (8, 5) - to the right of the player starting position
- Wall renders as
#character on screen, or a black square in wgpu rendering - Wall has no velocity (it doesn't move) - note that this could also be set to
VelocityComponent { x: 0, y: 0 }. - Wall has 2×1 size, so it appears as
##(2 units wide)
If you run now, you'll see the player move through the wall. Next, let's add collision detection to the game loop:
// ..
collision_pass()returns pairs of entities that are colliding- Iterate over collision pairs
[a, b] - When collision detected, immediately stop the player by resetting velocity to
(0, 0) - Critical: Call
ecs.step()AFTER collision detection to apply the movement
[!IMPORTANT] The order of operations in
next()matters! Update velocity → Check collisions → Apply movement. This prevents the player from "tunneling" through walls.
Now you'll see a ## wall that blocks your @ character's movement! Try moving right into it.
🎉 Congratulations! You’ve built a simple game with movement and collision using Hewn. Explore, experiment, and have fun making your own games! Check the examples or docs for more advanced features.
Step 5: Same Game, Desktop Window
Now we've built our game, it's possible to run in our WindowRuntime. Without changing our game, we use the wgpu runtime:
// ..
use WindowRuntime; // NEW!
// ..
- Swap
TerminalRuntimeforWindowRuntime- that's literally it!
Your @ character now renders as a colored square in a desktop window.
Platform Support
| Platform | Runtime | Command | Use Case |
|---|---|---|---|
| Terminal | TerminalRuntime |
cargo run |
ASCII games, debugging, servers |
| Desktop | WindowRuntime |
cargo run |
Native apps, high performance |
| Web | WASM + Canvas | wasm-pack build |
Browser games |
Architecture
Hewn games implement the GameHandler trait:
start_game()- Initialize your game statenext(dt: Duration)- Update game logic each frame with delta timehandle_key()- Process keyboard inputecs()- Access the Entity Component Systemdebug_str()- Show debug info (terminal only)
The ECS manages entities with components:
PositionComponent- Where entities are locatedVelocityComponent- How entities moveRenderComponent- How entities lookSizeComponent- Entity collision boundsCameraFollow- Camera tracks this entity
Examples
Run the Built-in Examples
# Terminal snake game
# Terminal platformer
# Terminal flying game
Web Deployment
# Install wasm-pack if you haven't
|
# Build any example for web
# Serve locally
# Open http://localhost:8000
Happy game making! 🎮