Developer Resources
Documentation
Everything you need to build games for Nethercore
Start Here
Build Paddle in 8 Parts
Learn Nethercore by building a complete game from scratch. Covers drawing, input, physics, AI, multiplayer, audio, and publishing.
Start the TutorialBrowse
Documentation Sections
Getting Started
Set up your development environment and create your first game.
- Prerequisites
- Your First Game
- Understanding the Game Loop
Tutorials
Step-by-step guides to building complete games.
- Build Paddle (8 Parts)
- Drawing & Input
- Multiplayer & Sound
Guides
In-depth explanations of Nethercore features and best practices.
- Render Modes
- Asset Pipeline
- Publishing Your Game
API Reference
Complete FFI function reference with examples.
- Cheat Sheet
- Input Functions
- Graphics Functions
Examples
28+ example games organized by feature and complexity.
- Learning Path
- Graphics Examples
- Complete Games
Source Code
Browse the Nethercore source, file issues, and contribute.
- GitHub Repository
- Issue Tracker
- Discussions
Developer Book
The complete Nethercore developer documentation, hosted as an mdBook.
Open Full DocumentationQuick Reference
Common Patterns
Minimal Game
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
core::arch::wasm32::unreachable()
}
#[link(wasm_import_module = "env")]
extern "C" {
fn draw_rect(x: f32, y: f32, w: f32, h: f32, c: u32);
}
#[no_mangle]
pub extern "C" fn init() {}
#[no_mangle]
pub extern "C" fn update() {}
#[no_mangle]
pub extern "C" fn render() {
unsafe { draw_rect(100.0, 100.0, 50.0, 50.0, 0xFF6B6BFF); }
} #include
/* FFI imports */
EWZX_IMPORT void draw_rect(float x, float y, float w, float h, uint32_t c);
/* Game lifecycle */
EWZX_EXPORT void init(void) {}
EWZX_EXPORT void update(void) {}
EWZX_EXPORT void render(void) {
draw_rect(100.0f, 100.0f, 50.0f, 50.0f, 0xFF6B6BFF);
} // FFI imports
extern fn draw_rect(x: f32, y: f32, w: f32, h: f32, c: u32) void;
// Game lifecycle
export fn init() void {}
export fn update() void {}
export fn render() void {
draw_rect(100.0, 100.0, 50.0, 50.0, 0xFF6B6BFF);
} Rollback-Safe State
// All game state in static variables
static mut PLAYER_X: f32 = 100.0;
static mut PLAYER_Y: f32 = 200.0;
static mut SCORE: u32 = 0;
#[no_mangle]
pub extern "C" fn update() {
unsafe {
// Use synchronized random()
let spawn_x = (random() % 320) as f32;
// Deterministic updates
PLAYER_X += left_stick_x(0) * 5.0;
}
}
// Multiplayer just works! /* All game state in static variables */
static float player_x = 100.0f;
static float player_y = 200.0f;
static uint32_t score = 0;
EWZX_EXPORT void update(void) {
/* Use synchronized random_u32() */
float spawn_x = (float)(random_u32() % 320);
/* Deterministic updates */
player_x += left_stick_x(0) * 5.0f;
}
/* Multiplayer just works! */ // All game state in module-level variables
var player_x: f32 = 100.0;
var player_y: f32 = 200.0;
var score: u32 = 0;
export fn update() void {
// Use synchronized random_u32()
const spawn_x: f32 = @floatFromInt(random_u32() % 320);
// Deterministic updates
player_x += left_stick_x(0) * 5.0;
}
// Multiplayer just works! Asset Loading
static PLAYER_MESH: &[u8] = include_bytes!("assets/player.ewzmesh");
static GRASS_TEX: &[u8] = include_bytes!("assets/grass.ewztex");
fn init() {
let player = load_zmesh(
PLAYER_MESH.as_ptr() as u32,
PLAYER_MESH.len() as u32
);
let grass = load_ztex(
GRASS_TEX.as_ptr() as u32,
GRASS_TEX.len() as u32
);
} /* Embed assets at compile time */
extern const unsigned char player_ewzmesh_data[];
extern const unsigned int player_ewzmesh_size;
extern const unsigned char grass_ewztex_data[];
extern const unsigned int grass_ewztex_size;
EWZX_EXPORT void init(void) {
uint32_t player = load_zmesh(
(uint32_t)player_ewzmesh_data,
player_ewzmesh_size
);
uint32_t grass = load_ztex(
(uint32_t)grass_ewztex_data,
grass_ewztex_size
);
} const player_mesh = @embedFile("assets/player.ewzmesh");
const grass_tex = @embedFile("assets/grass.ewztex");
export fn init() void {
const player = load_zmesh(
@intFromPtr(player_mesh.ptr),
player_mesh.len
);
const grass = load_ztex(
@intFromPtr(grass_tex.ptr),
grass_tex.len
);
} Input Handling
fn update() {
// Button press (one-time trigger)
if button_pressed(0, BUTTON_A) != 0 {
player.jump();
}
// Analog stick with deadzone
let stick_x = left_stick_x(0);
let deadzone = 0.15;
if stick_x.abs() > deadzone {
player.x += stick_x * MOVE_SPEED * delta_time();
}
} EWZX_EXPORT void update(void) {
/* Button press (one-time trigger) */
if (button_pressed(0, EWZX_BUTTON_A)) {
player_jump();
}
/* Analog stick with deadzone */
float stick_x = left_stick_x(0);
float deadzone = 0.15f;
if (nczx_absf(stick_x) > deadzone) {
player_x += stick_x * MOVE_SPEED * delta_time();
}
} export fn update() void {
// Button press (one-time trigger)
if (button_pressed(0, Button.a) != 0) {
player_jump();
}
// Analog stick with deadzone
const stick_x = left_stick_x(0);
const deadzone = 0.15;
if (@abs(stick_x) > deadzone) {
player_x += stick_x * MOVE_SPEED * delta_time();
}
}