Skip to content

notnullgames/wgsleng

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WGSL Game Engine

A minimal game engine that runs games written entirely in WGSL (WebGPU Shading Language). Games are distributed as a single .wgsl file or a .zip containing main.wgsl and assets.

Features

  • Single-file games: Write your entire game in WGSL
  • Web runtime: Run games in any WebGPU-capable browser
  • Native runtime: Standalone Rust-based player for desktop
  • Simple input: Keyboard mapped to SNES controller (see Controls below)
  • Asset support: Include images and audio in zip format

Quick Start

Web

npm start

Controls

The engine maps keyboard keys to a SNES-style controller:

Button Key Color/Position
D-Pad Arrow keys Left side
A X Red, right
B Z Yellow, bottom
X S Blue, top
Y A Green, left
L Q Left shoulder
R W Right shoulder
SELECT Shift Bottom center
START Enter Bottom center

Examples

These all work local, and on the web:

  • bob: Demo with sprites and audio
  • bunny: Demo that loads a 3D model
  • cubespin: Simple spinning 3d cube
  • input: Provides some nice 2D drawing functions, and shows you the current state of the virtual controller
  • logo: Shows how to draw things without images
  • raymarch: Provides some nice 3D drawing functions with ray marching and SDF primitives
  • snake: Classic snake game
  • tetris: Classic tetris game. This also includes text-rendering. It's a bit obtuse, but works.

Native

You can find the CLI for your platform at releases.

Or if you want to build it yourself (requires rust):

# build the native CLI for your platform
npm run native

# load logo example, can also be a zip-file
./native/target/release/wgsleng examples/logo/main.wgsl

Creating Games

Games can be a single WGSL file or a zip archive containing:

  • main.wgsl (required)
  • Asset files (.png, .ogg, etc.)

See the examples/ directory for reference implementations.

Build example zips:

npm run game

image-only output

You might find it helpful to render WGSL to images (for LLM-comparison and things.) You can do that like this:

# output image of first-frame
npm run render examples/logo/main.wgsl /tmp/logo.png

# same, but also print the generated shader
DEBUG_SHADER=1 npm run render examples/logo/main.wgsl /tmp/logo.png

open /tmp/logo.png

extensions to WGSL

The engine works by adding some extensions to the language. Assets are referenced by filename. The idea is that some uniforms/shared-buffers are automatically setup for you and bound, so it all works without you having to manage loading assets. You are meant to be able to control the entire game, just from the main.wgsl.

// this is available in @engine
struct GameEngineHost {
    buttons: array<i32, 12>, // the current state of virtual SNES gamepad (BTN_*)
    time: f32, // clock time
    delta_time: f32, // time since last frame
    screen_width: f32, // current screensize
    screen_height: f32, // current screensize
    state: GameState, // user's game state that persists across frames
    audio: array<u32, {SIZE}>, // audio trigger counters
}

// Button constants for input
const BTN_UP: u32 = 0u;
const BTN_DOWN: u32 = 1u;
const BTN_LEFT: u32 = 2u;
const BTN_RIGHT: u32 = 3u;
const BTN_A: u32 = 4u;
const BTN_B: u32 = 5u;
const BTN_X: u32 = 6u;
const BTN_Y: u32 = 7u;
const BTN_L: u32 = 8u;
const BTN_R: u32 = 9u;
const BTN_START: u32 = 10u;
const BTN_SELECT: u32 = 11u;


// Define this struct for holding your data between frames
// You can put whatever you want in here
struct GameState {
  player_pos: vec2f
}

// set the title of the window
@set_title("Bob-Bonker");

// set the size of the window, defaults to 800x600
@set_size(600, 600);

// inline the source of another file (from your zip/dir)
@import("helpers.wgsl");

// ASSETS
@sound("bump.ogg").play();
@sound("bump.ogg").stop();

let pos = @model("bunny.obj").positions[idx];
let normal = @model("bunny.obj").normals[idx];

let uv = (dist + 32.0) / 64.0;
let sprite = textureSampleLevel(@texture("player.png"), @engine.sampler, uv, 0.0);

asset credits

License

This project is licensed under the zlib/libpng License - a permissive open-source license that allows commercial use, modification, and redistribution with minimal restrictions.