diff --git a/docs/3pfollow.md b/docs/3pfollow.md
index 90850f64..f7fd490b 100644
--- a/docs/3pfollow.md
+++ b/docs/3pfollow.md
@@ -1,3 +1,7 @@
+---
+draft: true
+---
+
This was a good camera system for third person games. Recreate it eventually in cell.
// void ThirdPersonFollow::_ready() {
diff --git a/docs/api/actor.md b/docs/api/actor.md
index fdafbd98..b2743bda 100644
--- a/docs/api/actor.md
+++ b/docs/api/actor.md
@@ -1,3 +1,8 @@
+---
+title: "actor"
+type: docs
+---
+
# actor
### toString() function
diff --git a/docs/api/console.md b/docs/api/console.md
index a1a65453..30693e18 100644
--- a/docs/api/console.md
+++ b/docs/api/console.md
@@ -1,3 +1,8 @@
+---
+title: "console"
+type: docs
+---
+
# console
The console object provides various logging, debugging, and output methods.
diff --git a/docs/api/index.md b/docs/api/index.md
index 97e9273b..1ebd3906 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -1,5 +1,62 @@
-# Appendix B - api
+---
+title: "API Reference"
+type: docs
+---
-This is a complete list of accessible functions and parameters that are built into Prosperon. For the most part, developers will concern themselves with the modules, all of which can be imported with `use`.
+# API Reference
-Types document particular javascript objects with a specific object in their prototype chain, which can allow access to an underlying C data structure. A lot of these are used only internally by Prosperon, but brave developers can pick around in the module internals to see how they're used and do their own thing if they want!
\ No newline at end of file
+Prosperon's API is a set of modules imported with `use()`. There is no global engine object — import only what you need.
+
+## Game-Facing Modules
+
+These are the modules you use to build games:
+
+| Module | Import | Purpose |
+|--------|--------|---------|
+| **core** | `use('core')` | Main loop, window, GPU initialization |
+| **sprite** | `use('sprite')` | Create and manage sprites |
+| **compositor** | `use('compositor')` | Build render plans from scene configs |
+| **camera** | `use('camera')` | Viewport into the world |
+| **input** | `use('input')` | Action mapping, device routing, control stacks |
+| **sound** | `use('sound')` | Audio playback and mixing |
+| **world** | `use('world')` | Entity management |
+| **text2d** | `use('text2d')` | Text rendering |
+| **shape2d** | `use('shape2d')` | SDF-rendered shapes (rect, circle, ellipse, pill) |
+| **tilemap2d** | `use('tilemap2d')` | Grid-based tile rendering |
+| **line2d** | `use('line2d')` | Line rendering |
+| **particles2d** | `use('particles2d')` | Particle emitters |
+| **tween** | `use('tween')` | Value interpolation over time |
+| **ease** | `use('ease')` | Easing functions |
+| **color** | `use('color')` | Color utilities |
+| **resources** | `use('resources')` | Asset path resolution |
+| **action** | `use('action')` | Action definitions |
+| **draw2d** | `use('draw2d')` | Immediate-mode 2D drawing |
+| **clay** | `use('clay')` | UI layout |
+| **point** | `use('point')` | Point/vector utilities |
+
+## Actor Primitives
+
+These are built into the Pit language runtime and available in all `.ce` programs:
+
+| Primitive | Purpose |
+|-----------|---------|
+| `$start(callback, path)` | Spawn a child actor from a program |
+| `$receiver(callback)` | Register a message handler |
+| `send(target, message)` | Send a message to an actor |
+| `$delay(callback, seconds)` | Schedule a one-shot callback |
+| `$clock(callback, interval)` | Schedule a repeating callback |
+| `$stop()` | Terminate the current actor |
+
+## Globals
+
+| Name | Purpose |
+|------|---------|
+| `use(path)` | Import a module |
+| `log.console(msg)` | Print to console |
+| `meme(proto, data)` | Create object with prototype and overrides |
+
+## Internal Modules
+
+These are used by the engine internally and not intended for direct game use:
+
+`film2d`, `effects`, `sdl_gpu`, `graphics`, `rasterize`, `fx_graph`, `debug_imgui`, `device`, `input/backends/*`, `input/bindings`, `input/devices`, `input/router`
diff --git a/docs/api/modules/actor.md b/docs/api/modules/actor.md
index 5f3d9b8b..b3b4d881 100644
--- a/docs/api/modules/actor.md
+++ b/docs/api/modules/actor.md
@@ -1,3 +1,8 @@
+---
+title: "actor"
+type: docs
+---
+
# actor
diff --git a/docs/api/modules/camera.md b/docs/api/modules/camera.md
index 741ed994..c66b152d 100644
--- a/docs/api/modules/camera.md
+++ b/docs/api/modules/camera.md
@@ -1,3 +1,8 @@
+---
+title: "camera"
+type: docs
+---
+
# camera
### list() function
diff --git a/docs/api/modules/cmd.md b/docs/api/modules/cmd.md
index 46f5e07f..8f062c95 100644
--- a/docs/api/modules/cmd.md
+++ b/docs/api/modules/cmd.md
@@ -1,3 +1,8 @@
+---
+title: "cmd"
+type: docs
+---
+
# cmd
### length number
diff --git a/docs/api/modules/color.md b/docs/api/modules/color.md
index d12a896f..540eebd0 100644
--- a/docs/api/modules/color.md
+++ b/docs/api/modules/color.md
@@ -1,3 +1,8 @@
+---
+title: "color"
+type: docs
+---
+
# color
### Color object
diff --git a/docs/api/modules/debug.md b/docs/api/modules/debug.md
index c3017e44..54c9e5fd 100644
--- a/docs/api/modules/debug.md
+++ b/docs/api/modules/debug.md
@@ -1,3 +1,8 @@
+---
+title: "debug"
+type: docs
+---
+
# debug
### stack_depth() function
diff --git a/docs/api/modules/dmon.md b/docs/api/modules/dmon.md
index b14958ec..79ed6da8 100644
--- a/docs/api/modules/dmon.md
+++ b/docs/api/modules/dmon.md
@@ -1,3 +1,8 @@
+---
+title: "dmon"
+type: docs
+---
+
# dmon
### watch() function
diff --git a/docs/api/modules/doc.md b/docs/api/modules/doc.md
index ff7fae21..e5c50dcb 100644
--- a/docs/api/modules/doc.md
+++ b/docs/api/modules/doc.md
@@ -1,3 +1,8 @@
+---
+title: "doc"
+type: docs
+---
+
# doc
diff --git a/docs/api/modules/draw2d.md b/docs/api/modules/draw2d.md
index d1e27197..6b94b661 100644
--- a/docs/api/modules/draw2d.md
+++ b/docs/api/modules/draw2d.md
@@ -1,3 +1,8 @@
+---
+title: "draw2d"
+type: docs
+---
+
# draw2d
diff --git a/docs/api/modules/enet.md b/docs/api/modules/enet.md
index dd395cb1..035e1359 100644
--- a/docs/api/modules/enet.md
+++ b/docs/api/modules/enet.md
@@ -1,3 +1,8 @@
+---
+title: "enet"
+type: docs
+---
+
# enet
### initialize() function
diff --git a/docs/api/modules/event.md b/docs/api/modules/event.md
index 2b4a6e13..ccb128c0 100644
--- a/docs/api/modules/event.md
+++ b/docs/api/modules/event.md
@@ -1,3 +1,8 @@
+---
+title: "event"
+type: docs
+---
+
# event
### push_event(event) function
diff --git a/docs/api/modules/geometry.md b/docs/api/modules/geometry.md
index 26f2fb2b..d3bf86fc 100644
--- a/docs/api/modules/geometry.md
+++ b/docs/api/modules/geometry.md
@@ -1,3 +1,8 @@
+---
+title: "geometry"
+type: docs
+---
+
# geometry
diff --git a/docs/api/modules/graphics.md b/docs/api/modules/graphics.md
index 165270c5..98a2f03b 100644
--- a/docs/api/modules/graphics.md
+++ b/docs/api/modules/graphics.md
@@ -1,3 +1,8 @@
+---
+title: "graphics"
+type: docs
+---
+
# graphics
diff --git a/docs/api/modules/imgui.md b/docs/api/modules/imgui.md
index 4d129a85..361fc968 100644
--- a/docs/api/modules/imgui.md
+++ b/docs/api/modules/imgui.md
@@ -1,3 +1,8 @@
+---
+title: "imgui"
+type: docs
+---
+
# imgui
### windowpos() function
diff --git a/docs/api/modules/input.md b/docs/api/modules/input.md
index f31580f4..53cca6c4 100644
--- a/docs/api/modules/input.md
+++ b/docs/api/modules/input.md
@@ -1,3 +1,8 @@
+---
+title: "input"
+type: docs
+---
+
# input
### mouse_show(show) function
diff --git a/docs/api/modules/io.md b/docs/api/modules/io.md
index 23cbdb35..99761c37 100644
--- a/docs/api/modules/io.md
+++ b/docs/api/modules/io.md
@@ -1,3 +1,8 @@
+---
+title: "io"
+type: docs
+---
+
# io
### rm(path) function
diff --git a/docs/api/modules/js.md b/docs/api/modules/js.md
index af7b08f2..b051d10f 100644
--- a/docs/api/modules/js.md
+++ b/docs/api/modules/js.md
@@ -1,3 +1,8 @@
+---
+title: "js"
+type: docs
+---
+
# js
diff --git a/docs/api/modules/json.md b/docs/api/modules/json.md
index 572292b6..01dbc246 100644
--- a/docs/api/modules/json.md
+++ b/docs/api/modules/json.md
@@ -1,3 +1,8 @@
+---
+title: "json"
+type: docs
+---
+
# json
### encode(val,space,replacer,whitelist) function
diff --git a/docs/api/modules/loop.md b/docs/api/modules/loop.md
index cf3e6540..d9f35cf9 100644
--- a/docs/api/modules/loop.md
+++ b/docs/api/modules/loop.md
@@ -1,3 +1,8 @@
+---
+title: "loop"
+type: docs
+---
+
# loop
### step() function
diff --git a/docs/api/modules/math.md b/docs/api/modules/math.md
index 0574abc0..8ae190a1 100644
--- a/docs/api/modules/math.md
+++ b/docs/api/modules/math.md
@@ -1,3 +1,8 @@
+---
+title: "math"
+type: docs
+---
+
# math
### dot() function
diff --git a/docs/api/modules/miniz.md b/docs/api/modules/miniz.md
index 7a8ee0f8..060eb490 100644
--- a/docs/api/modules/miniz.md
+++ b/docs/api/modules/miniz.md
@@ -1,3 +1,8 @@
+---
+title: "miniz"
+type: docs
+---
+
# miniz
### read(data) function
diff --git a/docs/api/modules/nota.md b/docs/api/modules/nota.md
index 545e72a0..7daee187 100644
--- a/docs/api/modules/nota.md
+++ b/docs/api/modules/nota.md
@@ -1,3 +1,8 @@
+---
+title: "nota"
+type: docs
+---
+
# nota
### encode(value) function
diff --git a/docs/api/modules/os.md b/docs/api/modules/os.md
index 5ad4494e..dc8c87e1 100644
--- a/docs/api/modules/os.md
+++ b/docs/api/modules/os.md
@@ -1,3 +1,8 @@
+---
+title: "os"
+type: docs
+---
+
# os
### make_transform() function
diff --git a/docs/api/modules/packer.md b/docs/api/modules/packer.md
index 5db63459..ba734218 100644
--- a/docs/api/modules/packer.md
+++ b/docs/api/modules/packer.md
@@ -1,3 +1,8 @@
+---
+title: "packer"
+type: docs
+---
+
# packer
### getAllFiles(dir) function
diff --git a/docs/api/modules/render.md b/docs/api/modules/render.md
index 93e65537..7eeb3ed9 100644
--- a/docs/api/modules/render.md
+++ b/docs/api/modules/render.md
@@ -1,3 +1,8 @@
+---
+title: "render"
+type: docs
+---
+
# render
### _main object
diff --git a/docs/api/modules/resources.md b/docs/api/modules/resources.md
index a0af8811..94cc79a4 100644
--- a/docs/api/modules/resources.md
+++ b/docs/api/modules/resources.md
@@ -1,3 +1,8 @@
+---
+title: "resources"
+type: docs
+---
+
# resources
### scripts object
diff --git a/docs/api/modules/sound.md b/docs/api/modules/sound.md
index d7cd2afc..9df4160e 100644
--- a/docs/api/modules/sound.md
+++ b/docs/api/modules/sound.md
@@ -1,3 +1,8 @@
+---
+title: "sound"
+type: docs
+---
+
# sound
### undefined string
diff --git a/docs/api/modules/spline.md b/docs/api/modules/spline.md
index b070ddcb..6fd890ab 100644
--- a/docs/api/modules/spline.md
+++ b/docs/api/modules/spline.md
@@ -1,3 +1,8 @@
+---
+title: "spline"
+type: docs
+---
+
# spline
### catmull() function
diff --git a/docs/api/modules/time.md b/docs/api/modules/time.md
index 158567a9..7b6a9228 100644
--- a/docs/api/modules/time.md
+++ b/docs/api/modules/time.md
@@ -1,3 +1,8 @@
+---
+title: "time"
+type: docs
+---
+
# time
The main time object, handling date/time utilities in earth-seconds.
diff --git a/docs/api/modules/tween.md b/docs/api/modules/tween.md
index f987a249..80e474f2 100644
--- a/docs/api/modules/tween.md
+++ b/docs/api/modules/tween.md
@@ -1,3 +1,8 @@
+---
+title: "tween"
+type: docs
+---
+
# tween
### Tween object
diff --git a/docs/api/modules/util.md b/docs/api/modules/util.md
index 7f6f951c..3027aa21 100644
--- a/docs/api/modules/util.md
+++ b/docs/api/modules/util.md
@@ -1,3 +1,8 @@
+---
+title: "util"
+type: docs
+---
+
# util
diff --git a/docs/api/modules/video.md b/docs/api/modules/video.md
index 92ac692c..9bc0cabb 100644
--- a/docs/api/modules/video.md
+++ b/docs/api/modules/video.md
@@ -1,3 +1,8 @@
+---
+title: "video"
+type: docs
+---
+
# video
### make_video() function
diff --git a/docs/api/prosperon.md b/docs/api/prosperon.md
index f13284c8..7c8361b8 100644
--- a/docs/api/prosperon.md
+++ b/docs/api/prosperon.md
@@ -1,3 +1,8 @@
+---
+title: "prosperon"
+type: docs
+---
+
# prosperon
### c_types object
diff --git a/docs/api/types/PHYSFS_File.md b/docs/api/types/PHYSFS_File.md
index 46803890..ed9ddcd3 100644
--- a/docs/api/types/PHYSFS_File.md
+++ b/docs/api/types/PHYSFS_File.md
@@ -1,3 +1,8 @@
+---
+title: "PHYSFS_File"
+type: docs
+---
+
# PHYSFS_File
A file handle opened via PhysFS for writing or reading. Freed automatically when references go away.
diff --git a/docs/api/types/SDL_Camera.md b/docs/api/types/SDL_Camera.md
index de33f0fd..207a226b 100644
--- a/docs/api/types/SDL_Camera.md
+++ b/docs/api/types/SDL_Camera.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Camera"
+type: docs
+---
+
# SDL_Camera
A handle to a physical camera device. Freed when references drop or camera is closed.
diff --git a/docs/api/types/SDL_Cursor.md b/docs/api/types/SDL_Cursor.md
index 8731d8f4..d1bfd582 100644
--- a/docs/api/types/SDL_Cursor.md
+++ b/docs/api/types/SDL_Cursor.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Cursor"
+type: docs
+---
+
# SDL_Cursor
An SDL cursor handle. Freed automatically on GC. No direct methods.
diff --git a/docs/api/types/SDL_GPUBuffer.md b/docs/api/types/SDL_GPUBuffer.md
index 6c54bdf5..8eea31e9 100644
--- a/docs/api/types/SDL_GPUBuffer.md
+++ b/docs/api/types/SDL_GPUBuffer.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUBuffer"
+type: docs
+---
+
# SDL_GPUBuffer
### name() function
diff --git a/docs/api/types/SDL_GPUCommandBuffer.md b/docs/api/types/SDL_GPUCommandBuffer.md
index cebb8fa2..5377ad63 100644
--- a/docs/api/types/SDL_GPUCommandBuffer.md
+++ b/docs/api/types/SDL_GPUCommandBuffer.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUCommandBuffer"
+type: docs
+---
+
# SDL_GPUCommandBuffer
A command buffer that accumulates rendering, copy, and compute operations. Freed after submission or GC.
diff --git a/docs/api/types/SDL_GPUComputePass.md b/docs/api/types/SDL_GPUComputePass.md
index 99ea7abf..32d20547 100644
--- a/docs/api/types/SDL_GPUComputePass.md
+++ b/docs/api/types/SDL_GPUComputePass.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUComputePass"
+type: docs
+---
+
# SDL_GPUComputePass
A compute pass for dispatching compute pipelines. Freed after end() or GC.
diff --git a/docs/api/types/SDL_GPUComputePipeline.md b/docs/api/types/SDL_GPUComputePipeline.md
index 4ae86226..921482fa 100644
--- a/docs/api/types/SDL_GPUComputePipeline.md
+++ b/docs/api/types/SDL_GPUComputePipeline.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUComputePipeline"
+type: docs
+---
+
# SDL_GPUComputePipeline
Encapsulates a compute shader program plus associated resource layouts.
diff --git a/docs/api/types/SDL_GPUCopyPass.md b/docs/api/types/SDL_GPUCopyPass.md
index 10096344..26093620 100644
--- a/docs/api/types/SDL_GPUCopyPass.md
+++ b/docs/api/types/SDL_GPUCopyPass.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUCopyPass"
+type: docs
+---
+
# SDL_GPUCopyPass
A pass for CPU<->GPU or GPU<->GPU copy operations. No direct JS API besides internal usage.
diff --git a/docs/api/types/SDL_GPUDevice.md b/docs/api/types/SDL_GPUDevice.md
index 809e1f26..90f6bc33 100644
--- a/docs/api/types/SDL_GPUDevice.md
+++ b/docs/api/types/SDL_GPUDevice.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUDevice"
+type: docs
+---
+
# SDL_GPUDevice
A handle for low-level GPU operations via SDL GPU. Freed on GC.
diff --git a/docs/api/types/SDL_GPUFence.md b/docs/api/types/SDL_GPUFence.md
index d356e74d..981ff4e0 100644
--- a/docs/api/types/SDL_GPUFence.md
+++ b/docs/api/types/SDL_GPUFence.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUFence"
+type: docs
+---
+
# SDL_GPUFence
A GPU fence for synchronization. Created upon commandBuffer.submit().
diff --git a/docs/api/types/SDL_GPUGraphicsPipeline.md b/docs/api/types/SDL_GPUGraphicsPipeline.md
index 388e0ae8..b1e1ab0c 100644
--- a/docs/api/types/SDL_GPUGraphicsPipeline.md
+++ b/docs/api/types/SDL_GPUGraphicsPipeline.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUGraphicsPipeline"
+type: docs
+---
+
# SDL_GPUGraphicsPipeline
Encapsulates vertex+fragment shaders, blend/cull states, and vertex attribute layouts.
diff --git a/docs/api/types/SDL_GPURenderPass.md b/docs/api/types/SDL_GPURenderPass.md
index df258a7c..08c07f0c 100644
--- a/docs/api/types/SDL_GPURenderPass.md
+++ b/docs/api/types/SDL_GPURenderPass.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPURenderPass"
+type: docs
+---
+
# SDL_GPURenderPass
A single pass of drawing commands with color/depth attachments. Freed after end() or GC.
diff --git a/docs/api/types/SDL_GPUSampler.md b/docs/api/types/SDL_GPUSampler.md
index 521e0080..81454a90 100644
--- a/docs/api/types/SDL_GPUSampler.md
+++ b/docs/api/types/SDL_GPUSampler.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUSampler"
+type: docs
+---
+
# SDL_GPUSampler
Defines how a texture is sampled (filter mode, address mode, anisotropy, compare op, etc.).
diff --git a/docs/api/types/SDL_GPUShader.md b/docs/api/types/SDL_GPUShader.md
index 6aa5de6b..be7211ce 100644
--- a/docs/api/types/SDL_GPUShader.md
+++ b/docs/api/types/SDL_GPUShader.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUShader"
+type: docs
+---
+
# SDL_GPUShader
A single compiled shader (vertex or fragment) in a GPU-friendly format
diff --git a/docs/api/types/SDL_GPUTexture.md b/docs/api/types/SDL_GPUTexture.md
index fa1b2161..83028926 100644
--- a/docs/api/types/SDL_GPUTexture.md
+++ b/docs/api/types/SDL_GPUTexture.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUTexture"
+type: docs
+---
+
# SDL_GPUTexture
### name() function
diff --git a/docs/api/types/SDL_GPUTransferBuffer.md b/docs/api/types/SDL_GPUTransferBuffer.md
index 58124193..f2e788b8 100644
--- a/docs/api/types/SDL_GPUTransferBuffer.md
+++ b/docs/api/types/SDL_GPUTransferBuffer.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_GPUTransferBuffer"
+type: docs
+---
+
# SDL_GPUTransferBuffer
A staging buffer used for copying data to or from GPU buffers/textures. Typically
diff --git a/docs/api/types/SDL_Renderer.md b/docs/api/types/SDL_Renderer.md
index 69041077..c0e0a9a3 100644
--- a/docs/api/types/SDL_Renderer.md
+++ b/docs/api/types/SDL_Renderer.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Renderer"
+type: docs
+---
+
# SDL_Renderer
A 2D rendering context using the SDL renderer API. Freed automatically.
diff --git a/docs/api/types/SDL_Surface.md b/docs/api/types/SDL_Surface.md
index bb0662d8..7ff11cec 100644
--- a/docs/api/types/SDL_Surface.md
+++ b/docs/api/types/SDL_Surface.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Surface"
+type: docs
+---
+
# SDL_Surface
A software (CPU) image in memory. Freed when references vanish. Typically converted
diff --git a/docs/api/types/SDL_Texture.md b/docs/api/types/SDL_Texture.md
index 9b1c9289..ea5e394c 100644
--- a/docs/api/types/SDL_Texture.md
+++ b/docs/api/types/SDL_Texture.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Texture"
+type: docs
+---
+
# SDL_Texture
A 2D GPU-accelerated texture for rendering with SDL_Renderer. Freed automatically.
diff --git a/docs/api/types/SDL_Thread.md b/docs/api/types/SDL_Thread.md
index f1cc239e..8e3756ce 100644
--- a/docs/api/types/SDL_Thread.md
+++ b/docs/api/types/SDL_Thread.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Thread"
+type: docs
+---
+
# SDL_Thread
A handle to an SDL-created thread. Freed on GC after join.
diff --git a/docs/api/types/SDL_Window.md b/docs/api/types/SDL_Window.md
index 9a6d57d9..23f3dc32 100644
--- a/docs/api/types/SDL_Window.md
+++ b/docs/api/types/SDL_Window.md
@@ -1,3 +1,8 @@
+---
+title: "SDL_Window"
+type: docs
+---
+
# SDL_Window
An application window, created via prosperon.engine_start or SDL calls. Freed on GC.
diff --git a/docs/api/types/datastream.md b/docs/api/types/datastream.md
index 89204b0a..a22cf39a 100644
--- a/docs/api/types/datastream.md
+++ b/docs/api/types/datastream.md
@@ -1,3 +1,8 @@
+---
+title: "datastream"
+type: docs
+---
+
# datastream
A streaming media handle, typically for MPEG video. Freed automatically.
diff --git a/docs/api/types/enet_host.md b/docs/api/types/enet_host.md
index b402735f..f5489bad 100644
--- a/docs/api/types/enet_host.md
+++ b/docs/api/types/enet_host.md
@@ -1,3 +1,8 @@
+---
+title: "enet_host"
+type: docs
+---
+
# enet_host
### service(callback, timeout) function
diff --git a/docs/api/types/enet_peer.md b/docs/api/types/enet_peer.md
index e372d75f..c7664ccb 100644
--- a/docs/api/types/enet_peer.md
+++ b/docs/api/types/enet_peer.md
@@ -1,3 +1,8 @@
+---
+title: "enet_peer"
+type: docs
+---
+
# enet_peer
### send(data) function
diff --git a/docs/api/types/font.md b/docs/api/types/font.md
index 8a7bf576..5f23a7f2 100644
--- a/docs/api/types/font.md
+++ b/docs/api/types/font.md
@@ -1,3 +1,8 @@
+---
+title: "font"
+type: docs
+---
+
# font
A bitmap or TTF-based font object storing glyph data.
diff --git a/docs/api/types/rtree.md b/docs/api/types/rtree.md
index d936737c..781d9c12 100644
--- a/docs/api/types/rtree.md
+++ b/docs/api/types/rtree.md
@@ -1,3 +1,8 @@
+---
+title: "rtree"
+type: docs
+---
+
# rtree
An R-tree for spatial lookups. Insert bounding boxes, query by bounding box, etc.
diff --git a/docs/api/types/sprite.md b/docs/api/types/sprite.md
index 27354d1a..0ed86c06 100644
--- a/docs/api/types/sprite.md
+++ b/docs/api/types/sprite.md
@@ -1,3 +1,8 @@
+---
+title: "sprite"
+type: docs
+---
+
# sprite
A 'sprite' is a simple struct for 2D drawing. It stores a rectangle (pos + size),
diff --git a/docs/api/types/timer.md b/docs/api/types/timer.md
index 6a39a91e..5a19483a 100644
--- a/docs/api/types/timer.md
+++ b/docs/api/types/timer.md
@@ -1,3 +1,8 @@
+---
+title: "timer"
+type: docs
+---
+
# timer
A scheduled callback or countdown. Freed automatically once no longer referenced
diff --git a/docs/api/types/transform.md b/docs/api/types/transform.md
index 1f94fdf0..739f854d 100644
--- a/docs/api/types/transform.md
+++ b/docs/api/types/transform.md
@@ -1,3 +1,8 @@
+---
+title: "transform"
+type: docs
+---
+
# transform
A hierarchical transform storing 3D or 2D position, rotation (as a quaternion),
diff --git a/docs/api/use.md b/docs/api/use.md
index cf3ad058..07115b94 100644
--- a/docs/api/use.md
+++ b/docs/api/use.md
@@ -1,3 +1,8 @@
+---
+title: "use"
+type: docs
+---
+
# use
### length number
diff --git a/docs/entities.md b/docs/entities.md
new file mode 100644
index 00000000..bc037daa
--- /dev/null
+++ b/docs/entities.md
@@ -0,0 +1,121 @@
+---
+title: "Entities"
+type: docs
+---
+
+# Entities and the World
+
+Prosperon uses a **script + overrides** model for entities, similar to Source engine entities. An entity type is defined by a script (`.cm` module), and instances are records that override specific attributes.
+
+## Entity Types
+
+An entity type is a module that returns a prototype object:
+
+```javascript
+// entities/goblin.cm
+var sprite = use('sprite')
+
+return {
+ health: 100,
+ speed: 2,
+ image: "goblin.png",
+
+ init: function() {
+ this.sprite = sprite({
+ image: this.image,
+ pos: this.pos,
+ width: 32,
+ height: 32,
+ layer: 5
+ })
+ },
+
+ on_destroy: function() {
+ this.sprite.destroy()
+ }
+}
+```
+
+The return value defines every valid field with a default. This prototype IS the schema — the editor and tooling can introspect it to know what fields exist and what their defaults are.
+
+## Creating Entities
+
+Use the `world` module to create entity instances:
+
+```javascript
+var world = use('world')
+var goblin_proto = use('entities/goblin')
+
+var goblin = world.add_entity(goblin_proto, {
+ pos: {x: 100, y: 200},
+ health: 50
+})
+```
+
+The second argument overrides specific fields from the prototype. Unspecified fields keep their defaults.
+
+## Entity Lifecycle
+
+Entities have two lifecycle hooks:
+
+- `init()` — called when the entity is created (after overrides are applied)
+- `on_destroy()` — called when the entity is removed from the world
+
+```javascript
+world.destroy_entity(goblin)
+```
+
+## Levels as Data
+
+A level is a collection of entity instances stored as JSON — each entry is a script path plus attribute overrides:
+
+```json
+[
+ {"script": "entities/goblin", "pos": {"x": 100, "y": 200}, "health": 50},
+ {"script": "entities/goblin", "pos": {"x": 300, "y": 200}},
+ {"script": "entities/tree", "pos": {"x": 200, "y": 150}},
+ {"script": "entities/player_spawn", "pos": {"x": 50, "y": 50}}
+]
+```
+
+The world loads this file, creates each entity from its prototype with the overrides applied. Since levels are JSON, they're easy to diff in source control and simple to generate from an editor.
+
+## Composition via Modules
+
+Entity behavior comes from composition, not inheritance. A goblin that patrols and has health uses separate modules:
+
+```javascript
+// entities/patrol_goblin.cm
+var health = use('systems/health')
+var patrol = use('systems/patrol')
+
+return {
+ health: 100,
+ patrol_speed: 1.5,
+ patrol_points: [],
+
+ init: function() {
+ health.attach(this)
+ patrol.attach(this, this.patrol_points)
+ }
+}
+```
+
+Each system module provides behavior that operates on the entity's data. No class trees, no diamond inheritance — just modules that read and write fields.
+
+## Querying Entities
+
+The world module lets you find entities:
+
+```javascript
+var world = use('world')
+
+// All entities are tracked internally
+// Query patterns are still being developed
+```
+
+## Override Rules
+
+Overrides replace fields at the top level. If the prototype has `pos: {x: 0, y: 0}` and the override has `pos: {x: 50, y: 100}`, the entire `pos` is replaced. This is predictable and avoids ambiguity about partial nested merges.
+
+Overrides should be pure data — field values only. No expressions, no conditionals, no logic. The script defines behavior; the override only tweaks data.
diff --git a/docs/graphics.md b/docs/graphics.md
index 22026a62..0eca043d 100644
--- a/docs/graphics.md
+++ b/docs/graphics.md
@@ -1,152 +1,189 @@
-# Graphics drawing
+---
+title: "Graphics"
+type: docs
+---
-## Terminology
-Texture - a set of bytes on the GPU, not directly accessible
-Surface - a set of bytes in RAM, modifiable
-rect - a rectangle of {x,y,width,height}
-Image - combination of a texture and rect, where the rect defines the UV coordinates on the texture to draw
+# Graphics Concepts
-# Drawing, cameras, viewports, logical size, and so on
+This page covers the fundamental graphics concepts in Prosperon.
-A camera is a view into the game world. A camera can be "rendered", which means it renders the world, and what it can see in the world. A camera may draw to a surface, or to the main window. Objects in the world will render so that if their position is equal to the camera position, that is in the center of the screen. HUD functions always render so [0,0] is the bottom left of the camera's view.
+## Textures and Images
-Cameras always draw to their own render target. Then, they draw that render target to the framebuffer.
+A **texture** is a set of bytes on the GPU — not directly accessible from script code.
-# COORDINATES
-Screen coordinates start in the upper left corner at [0,0] and extend to the bottom right, in pixels. Raw mouse coordinates are in these.
+An **image** is a texture combined with a UV rect that specifies which region to draw:
-# RENDERING PIPELINE
+```
+image = {
+ texture: GPU texture handle,
+ rect: {x, y, width, height} // UV coordinates
+}
+```
-In prosperon, you call graphics rendering functions at well defined hook points. These are interleaved as necessary with predefined created objects, like sprites, 3d world models, and so on.
+This means multiple images can share one texture (a sprite sheet or atlas), each referencing a different region.
-The engine stores a command buffer. When you issue "draw" commands, they are recorded into the command buffer. These are batched as much as they can be; if there is no significant state change between, the draw commands can be coalesced into one. Then, for each camera, the draw commands are executed.
+## Supported Image Formats
-# RENDERING COMPONENTS
-## MATERIALS
-A material defines the inputs to a shader.
+| Format | Notes |
+|--------|-------|
+| PNG | Standard, lossless |
+| QOI | Fast decode, lossless |
+| GIF | Animated frames supported |
+| JPG/JPEG | Lossy |
+| Aseprite (.ase) | Frames, tags, slices, durations, pivots |
-## PIPELINES
-Pipelines are how the rendering engine is set up. Switching pipelines can be done for special effects.
+Aseprite files are fully parsed — named animation tags, per-frame durations, pivot points, and slice data are all available.
-## SPECIAL EFFECTS
-Sometimes you want a special effect. While there are many objects in prosperon you can create and have the engine handle for you, a special effect typically requires a bit of code.
+## Sprites
-# LAYERS
-All things that draw have a layer. If no layer is set, the implicit layer is "0". Even draw and hud functions have a layer. To draw a draw function on a specific layer, set that function's "layer". ie,
+A sprite is the fundamental drawable. Create one with the `sprite` module:
-this.draw = function() { render.rect(); }
-this.draw.layer = -5;
+```javascript
+var sprite = use('sprite')
-Now that layer will draw at the -5 layer.
+var s = sprite({
+ image: "player.png",
+ pos: {x: 100, y: 200},
+ width: 32,
+ height: null, // derived from aspect ratio
+ anchor_x: 0.5, // center horizontally
+ anchor_y: 0, // bottom edge
+ layer: 5,
+ rotation: 0,
+ flip: {x: false, y: false},
+ color: {r: 1, g: 1, b: 1, a: 1},
+ opacity: 1,
+ tint: {r: 1, g: 1, b: 1, a: 1},
+ filter: 'nearest',
+ plane: 'default',
+ groups: [],
+ visible: true
+})
+```
-# CAMERAS
-Everything is drawn via cameras. Cameras can draw directly to the screen, or they can draw to an offscreen render target. By default, everything is drawn to all cameras. There will eventually be a tag that lets you filter what is drawn to specifc cameras.
+### Fit Modes
-Cameras have a resolution they draw at, "size".
+When both `width` and `height` are specified, the `fit` property controls how the texture maps into the rectangle:
-## TEXTURES
+| Fit | Behavior |
+|-----|----------|
+| `none` | Use the texture's native pixel size |
+| `fill` | Stretch to exactly fill the rectangle (may distort) |
+| `contain` | Fit inside the rectangle, preserving aspect ratio |
+| `cover` | Fill the rectangle, preserving aspect ratio (crops via UV) |
-Anatomy of rendpering an image render.image(path)
-Path can be a file like "toad"
-If this is a gif, this would display the entire range of the animation
-It can be a frame of animation, like "frog.0"
-If it's an aseprite, it can have multiple animations, like "frog.walk.0"
- file^ frame^ idx
+If only one dimension is given (e.g. `width: 32, height: null`), the other is derived from the texture's aspect ratio.
-render.image("frog.walk.0",
-game.image("frog.walk.0") ==> retrieve
+### Anchors
- image = {
- texture: "spritesheet.png",
- rect: [x,y,w,h],
- time: 100
- },
+The anchor determines which point of the sprite sits at `pos`:
- frames: {
- toad: {
- x: 4,
- y: 5,
- w: 10,
- h: 10
- },
- frog: {
-
- walk: [
- { texture: spritesheet.png, x: 10, y:10, w:6,h:6, time: 100 },
- { texture: spritesheet.png, x:16,y:10,w:6,h:6,time:100} <--- two frame walk animation
- ],
- },
- },
- }
- texture frog {
- texture: {"frog.png"}, <--- this is the actual thing to send to the gpu
- x:0,
- y:0,
- w:10,
- h:10
- },
+- `anchor_x: 0, anchor_y: 0` — top-left at pos
+- `anchor_x: 0.5, anchor_y: 0.5` — center at pos
+- `anchor_x: 0.5, anchor_y: 0` — bottom-center at pos (good for characters)
+### UV Mapping
-## RENDER MODES
-/* rendering modes
- ps1
- gouraud
- diffuse // 16 bit color, 5-5-5
- 7 dynamic lights, 1 ambient
- textures are affine
- no vertex skinning
- 256x256 texture max (generally 128x128)
- 320x240, variable up to 640x480
+For sprite sheets, control which region of the texture is drawn:
- n64
- gouraud
- diffuse
- combiner // a secondary texture sometimes used to combine
- 7 dynamic lights, 1 ambient
- 320x240, or 640x480
+```javascript
+sprite({
+ image: spritesheet_texture,
+ uv: {offset: {x: 0.25, y: 0}, scale: {x: 0.25, y: 0.5}}
+})
+```
- sega saturn
- gouraud
- diffuse
- 320x240 or 640x480
+## Text
- ps2
- phong
- diffuse
- combiner // second texture for modulation of diffuse
- combine_mode // int for how to combine
+Create text with the `text2d` module:
- dreamcast
- phong
- diffuse
- combiner // second texture; could be an environment map, or emboss bump mapping
- fog
- 640x480
- 640x448, special mode to 1280x1024
+```javascript
+var text2d = use('text2d')
- gamecube
- phong
- diffuse
- +7 textures // wow!
- 8 dynamic lights
- 640x480
+var label = text2d({
+ text: "Hello World",
+ font: "myfont.ttf",
+ size: 24,
+ pos: {x: 100, y: 100},
+ layer: 10
+})
+```
-*/
+## Shapes
-/* meshes
- position (float3)
- color (rgba)
- uv
-*/
+SDF-rendered shapes via the `shape2d` module:
-/* materials, modern pbr
- any object can act as a "material". The engine expects some standardized things:
- diffuse - base color texture
- bump - a normal map for dot3 bump maping used in phong shading
- height - a grayscale heightmap
- occlusion - ambient occlusion texture
- emission - texture for where model emits light
- bump2 - a second normal map for detail
- metallic - a metal/smoothness map
- specular - specular map, alternative for the metallic workflow
-*/
+```javascript
+var shape2d = use('shape2d')
+
+var box = shape2d.rect({
+ pos: {x: 50, y: 50},
+ width: 100,
+ height: 80,
+ color: {r: 1, g: 0, b: 0, a: 1},
+ layer: 3
+})
+
+var ball = shape2d.circle({
+ pos: {x: 200, y: 200},
+ radius: 25,
+ color: {r: 0, g: 1, b: 0, a: 1}
+})
+```
+
+Available shapes: `rect`, `circle`, `ellipse`, `pill`.
+
+## Tilemaps
+
+Grid-based tile rendering:
+
+```javascript
+var tilemap2d = use('tilemap2d')
+
+var map = tilemap2d({
+ tiles: tiles_2d_array,
+ tile_width: 16,
+ tile_height: 16,
+ layer: 0
+})
+```
+
+Tiles can be flipped, and the entire tilemap can have an affine transform applied. Camera culling for large maps is planned.
+
+## Particles
+
+Particle emitters produce sprite-like particles:
+
+```javascript
+var particles2d = use('particles2d')
+
+var emitter = particles2d({
+ texture: "spark.png",
+ pos: {x: 100, y: 100},
+ layer: 8
+})
+```
+
+## Coordinate System
+
+Prosperon uses a Y-up coordinate system:
+
+- **X+** goes right
+- **Y+** goes up
+- `[0, 0]` in world space is where the camera starts
+- `[0, 0]` in HUD space is the bottom-left of the screen
+
+## Camera
+
+A camera defines the viewport into the world. Its `width` and `height` set the game's internal resolution. Its `pos` determines what world coordinate is at the center of the screen.
+
+```javascript
+var camera = use('camera')
+var cam = camera.make({
+ pos: {x: 0, y: 0},
+ width: 320,
+ height: 240
+})
+```
+
+Everything in world space is drawn relative to the camera's position. HUD space is always screen-relative.
diff --git a/docs/index.md b/docs/index.md
index d04be64b..6a9de811 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,32 +1,59 @@
-# Preface: The Prosperon Vision
+---
+title: "Prosperon Manual"
+type: docs
+---
-
+# Prosperon
-Prosperon is based around a Javascript-like language, DullJS, termed henceforth "dull", engineered to be the quickest way to make computer games. "Dull" will be explored in more detail, but for all intents and purposes it **is** Javascript, with a handful of ES6 features removed. We've given it its own name to crush from the outset any idea that what you're dealing with a web styled Javascript environment!
+Prosperon is a 2D game programming environment built on [Pit](https://crumbpit.org), an actor-based language designed for safe, sandboxed scripting.
-## The Vision
-The less lines of code it takes to do something, the less your program does. The less your program does, the faster it is, and the fewer bugs it has. And, the less the developer needs to keep in their head, the less bugs and more understandable a program is.
+You write game logic in Pit — a simplified JavaScript with actor primitives — and Prosperon handles rendering, input, audio, and asset management. Games are composed from small, focused modules. No class hierarchies, no giant scene trees.
-Prosperon is designed to achieve a minimal number of lines of code when creating a game. Prosperon is designed to be the **best** way to make computer games, quickly.
+## How It Works
-1. **Flexible API**
-A lot of the API usage is informed from 'duck typing'. If something "looks" like a camera - it can be used like one! If something "looks" like a sprite, it can be used like one! This means that if you create an enemy in the game, you may be able to pass it in to render, while in other more strict languages, you might need to create an intermediate "sprite" object.
+A Prosperon game is a collection of **modules** (`.cm`) and **programs** (`.ce`).
-2. **Uniformity**
-Javascript has the brilliant object idea, which is used and abused everywhere in prosperon. Objects allow for a huge variety of ideas to be expressed, and act as an ideal transferring tool between the disparate parts of a computer game. Dull, our dumbed down javascript, uses objects and closures to a greater extent than modern javascript, **without** losing any of the features. There is less syntax to remember, meaning it is quicker to get code down.
+- **Modules** return a frozen value — an API, a config, a data table. Import them with `use()`.
+- **Programs** are actor entry points. They run top-to-bottom, register message handlers, and spawn children.
-3. **Highly reflective**
-Built in tools to give an overview of your game. Prosperon can tell you where and how many certain entities are created, how much space they use, and so on. Prosperon can generate an textual overview of your game, resulting in documentation.
+The engine provides modules for everything a 2D game needs:
-4. **Text based**
-Prosperon is totally text based. It uses a script based scene structure, where scripts spawn each other and concatenate onto each other to create objects in a scene in a flexible way. This allows for a first class experience with SCM tools, and allows for easy multi user collaboration.
+| Module | Purpose |
+|--------|---------|
+| `sprite` | Create and register sprites |
+| `draw2d` | High-level factories for sprites, text, shapes, tilemaps |
+| `camera` | Create cameras with transform and projection |
+| `input` | Multi-device action mapping with control stacks |
+| `sound` | Audio playback with PCM caching and voice mixing |
+| `tween` | Fire-and-forget animation with easing |
+| `world` | Entity management and lifecycle |
+| `resources` | Asset discovery and path resolution |
+| `core` | Main loop, window, frame scheduling |
-4. **Gradual Performance**
-There are fast paths on nearly everything for well defined objects. For most use cases, the flexible, duck typing works. But in specific circumstances, where speed is required, it's possible.
+You create sprites, poke their positions, and the engine handles everything else — batching, sorting, effects, presentation scaling. Like programming sprites on a GBA, but with modern hardware behind it.
-5. **Interactive**
-Whenever there is a question of feature, Prosperon chooses the interactive path. Games are interactive. That means you want it to be totally dynamic and fast. Prosperon is designed to favor, for example, generated MIDI music, which you might be able to sync to goblin death animations, over static MP3 files.
+## Design Principles
-## Installation
+**Minimal code.** The fewer lines it takes to do something, the fewer bugs it has, the faster it runs, and the less you need to keep in your head.
-Just grab the prosperon build for your platform, drop it in a folder, and run it. Prosperon is a tiny executable, so it's recommended to version it with your project.
+**Duck typing.** If an object looks like a sprite — has a position, an image, a layer — it works as a sprite. No base classes to inherit from.
+
+**Text-based.** Every file in a Prosperon project is text. Scripts, configs, levels. Git diffs are readable, merges are clean.
+
+**Modules, not globals.** Everything is accessed via `use()`. No global engine object. The world, camera, input — all imported as modules.
+
+**Data-driven rendering.** Your game logic never touches the GPU. You declare drawables and their properties. The rendering pipeline sorts, batches, applies effects, and draws.
+
+## Guides
+
+- [Quickstart](quickstart/) — Run your first program
+- [Tutorial](tutorial/) — Build a game step by step
+- [Entities](entities/) — The world and entity model
+- [Rendering](rendering/) — Sprites, cameras, and the rendering pipeline
+- [Graphics](graphics/) — Textures, images, and coordinate systems
+- [Input](input/) — Action mapping, devices, and control stacks
+- [Resources](resources/) — Asset loading and discovery
+
+## API Reference
+
+- [API Index](api/) — All modules and types
diff --git a/docs/input.md b/docs/input.md
index 705fb689..e8474c8c 100644
--- a/docs/input.md
+++ b/docs/input.md
@@ -1,54 +1,148 @@
-# Program events and an example input system
+---
+title: "Input"
+type: docs
+---
-Prosperon provides a handy `input` module. Input is done in a highly generic and customizable manner. *players* can take control of any object (actor or otherwise) in Prosperon, after which it is referred to as a *pawn* of a player. If the object has a defined *input* object, it is a valid pawn. One player can have many pawns, but each pawn may have only one player.
+# Input
-Pawns are added as a stack, with the newest ones getting priority, and handled first. It is possible for pawns to block input to lower pawns on the stack.
+Prosperon's input system normalizes keyboard, mouse, gamepad, and touch into **named actions**. You define what actions your game uses, bind them to physical inputs, and the engine routes events to the right place.
-```
-*newest*
-car <== When a key is pressed, this is the first pawn to handle input
-player
-ui <== /block/ is set to true here, so editor recieves no input!
-editor
-*oldest*
-```
+## Actions
-The default player can be obtained with `Player.players[0]`. Players are all local, and the highest number is determined by platform.
+An action is a named game event like `"jump"`, `"attack"`, or `"ui_up"`. Multiple physical inputs can trigger the same action:
-The **input** object defines a number of keys or actions, with their values being functions.
-
-## Editor input
-The editor input style defines keystrokes. It is good for custom editors, or any sort of game that requires many hotkeys. Keystrokes are case sensitive and can be augmented with auxiliary keys.
-
-| symbol | key |
-|--------|-------|
-| C | ctrl |
-| M | alt |
-| S | super |
-
-```
+```javascript
var input = use('input')
-var orc = this.spawn('orc');
-orc.inputs = {};
-orc.inputs.a = function() { ... };
-orc.inputs.A = function() { ... }; /* This is only called with a capital A! */
-orc.inputs['C-a'] = function() { ... }; /* Control-a */
-input.players[0].control(orc); /* player 0 is now in control of the orc */
+
+input.configure({
+ action_map: {
+ jump: ['space', 'gamepad_a'],
+ attack: ['mouse_button_left', 'gamepad_x'],
+ move_left: ['a', 'left', 'gamepad_dpleft'],
+ move_right: ['d', 'right', 'gamepad_dpright']
+ }
+})
```
-The input object can be modified to customize how it handles input.
+Default actions are provided for common UI navigation (`ui_up`, `ui_down`, `ui_left`, `ui_right`, `confirm`, `cancel`, `menu`).
-| property | type | effect |
-|----------------|----------|--------------------------------------|
-| post | function | called after any input is processed |
-| =release_post= | function | called after any input is released |
-| fallthru | bool | false if input should stop with this |
-| block | bool | true if input should stop with this |
+## Players and Users
-The input can be modified by setting properties on the associated function.
+The input system supports multiple players. Each **user** has:
-| property | type | effect |
-|----------|----------|--------------------------------------------------------|
-| released | function | Called when the input is released |
-| rep | bool | true if holding the input should repeatedly trigger it |
-| down | function | called while the input is down |
+- Paired devices (keyboard, specific gamepad, etc.)
+- An action map (bindings can differ per player)
+- A **control stack** of possessed entities
+
+```javascript
+var p1 = input.player1()
+```
+
+### Device Pairing
+
+By default, pairing is `'last_used'` — all input goes to player 1, and the active device switches when a button is pressed. For local multiplayer, use `'explicit'` pairing where each player is assigned specific devices.
+
+```javascript
+input.configure({
+ max_users: 2,
+ pairing: 'explicit'
+})
+```
+
+## Control Stack (Possession)
+
+Players **possess** entities to route input to them. The control stack is ordered — the topmost entity gets input first.
+
+```javascript
+var p1 = input.player1()
+
+// Take control of the player character
+p1.possess(player_entity)
+
+// Push a menu on top — it gets input priority
+p1.push(pause_menu)
+
+// Pop the menu — player character gets input again
+p1.pop()
+```
+
+The possessed entity must have an `on_input` method:
+
+```javascript
+var player = {
+ on_input: function(action, data) {
+ if (action == 'jump' && data.pressed) {
+ // jump logic
+ }
+ if (action == 'move_right') {
+ // data.pressed, data.released, data.time
+ }
+ }
+}
+```
+
+## Device Detection
+
+The input system tracks what kind of device is active, so you can show appropriate button prompts:
+
+```javascript
+var p1 = input.player1()
+var kind = p1.device_kind() // 'keyboard', 'mouse', 'gamepad'
+var type = p1.gamepad_type() // 'xbox', 'playstation', etc.
+```
+
+Get the display name or icon for an action based on the current device:
+
+```javascript
+var icon = p1.get_icon_for_action('jump')
+var binding = p1.get_primary_binding('jump')
+```
+
+## Emacs-Style Keybindings
+
+For editors or complex hotkey systems, Prosperon supports modifier notation:
+
+| Prefix | Key |
+|--------|-----|
+| `C-` | Ctrl |
+| `M-` | Alt |
+| `S-` | Super |
+
+So `C-a` means Ctrl+A, `C-M-s` means Ctrl+Alt+S. Case is preserved — `a` and `A` are different bindings.
+
+The emacs module is enabled by default but can be disabled:
+
+```javascript
+input.configure({ emacs: false })
+```
+
+## Gestures
+
+Touch gesture recognition is built in:
+
+- Swipe detection (with configurable distance and time thresholds)
+- Pinch detection
+
+```javascript
+input.configure({
+ gestures: true,
+ swipe_min_dist: 50,
+ swipe_max_time: 0.5
+})
+```
+
+## Saved Bindings
+
+Player bindings are automatically saved and loaded, so remapped controls persist across sessions.
+
+## Raw Events
+
+For debugging or special cases, the raw SDL event can be accessed via the config callback:
+
+```javascript
+core.start({
+ input: function(raw_event) {
+ // raw SDL event object
+ }
+})
+```
diff --git a/docs/ops.md b/docs/ops.md
index 4701e9b1..2baf6bd4 100644
--- a/docs/ops.md
+++ b/docs/ops.md
@@ -1,186 +1,170 @@
-# RENDERING PIPELINE
-The basic flow for developing graphics here:
+---
+title: "Compositor"
+type: docs
+---
-1) develop a render graph
-2) decide what to draw
+# The Compositor
-The render graph is the "big idea" of how the data flows through a render; inside the execution, you utilize "what to draw".
+The compositor is Prosperon's rendering orchestrator. You describe your scene — planes, layers, effects — and the compositor figures out what render passes are needed and executes them.
-Prosperon provides you with functions to facilitate the creation of rendering pipelines. For example, you could use "shadow_vol" function to create buffer geometry with shadow volume data.
+## The Basic Idea
-Unity has a "graphics.rendermesh" function that you can call, and that unity automatically calls for renderer components. It is the same here. But there are a handful of other types to draw, particularly for 2d.
+You don't issue draw calls. You create sprites and set their properties. The compositor does the rest:
-## 2D
-### Anatomy of a 2d renderer
-Traditionally, 2d rendering is a mix of tilemaps and sprites. Today, it is still more cost effective to render tilemaps, but we have a lot more flexibility.
+1. Queries all registered drawables
+2. Organizes them by plane and layer
+3. Applies effects to tagged groups
+4. Composites everything to the screen
-NES
-Nes had 1 tilemap and up to 8 sprites per scanline.
+Think of it like a GBA: you declare "sprite here, sprite there," get handles back, and poke their positions. The hardware (in this case, the compositor + GPU backend) handles the actual rendering.
-SNES
-Up to 4 tilemap backgrounds, with priority, and flipping capability. 32 sprites per scanline, and by setting the priority correctly, they could appear behind background layers.
+## Scene Configuration
-GB
-One background layer, 10 sprites per scanline/40 per frame.
+A scene is described as a config object passed to the compositor:
-GBA
-Up to 4 layers, sprites with affine transforms!
+```javascript
+var compositor = use('compositor')
-DS
-Up to 4 layers; many sprites; and a 3d layer!
+var plan = compositor.compile({
+ clear: {r: 0, g: 0, b: 0, a: 1},
+ planes: [
+ {
+ name: 'background',
+ plane: 'background',
+ resolution: {width: 320, height: 240},
+ camera: bg_camera,
+ presentation: 'integer_scale'
+ },
+ {
+ name: 'game',
+ plane: 'default',
+ resolution: {width: 320, height: 240},
+ camera: game_camera,
+ layer_sort: {'5': 'y'},
+ presentation: 'integer_scale'
+ },
+ {
+ name: 'hud',
+ plane: 'hud',
+ resolution: {width: 1280, height: 720},
+ camera: hud_camera,
+ presentation: 'stretch'
+ }
+ ]
+})
+```
-Sega saturn
-This and everything else with generic vertex processing could do as many background layers and sprites as desired. This is what you get with prosperon on most modern computers. For more limited hardware, your options become limited too!
+Each plane renders independently at its own resolution and composites onto the screen.
-### Prosperon rendering
-Layers
-Every drawable 2d thing has a layer. This is an integer that goes from -9223372036854775808 to 9223372036854775808.
+## Planes
-!!! On hardware that supports only a limited number of layers, this value must go from 0 to (layer #).
+A **plane** is a named rendering group. Sprites belong to a plane via their `plane` property (default: `'default'`). Each plane in the compositor config:
-Layer sort
-Within a layer, objects are sorted based on a given criteria. By default, this is nothing, and the engine may reorder the draws to optimize for performance. Instead, you can choose to sort by their y axis position, for example.
+- Has its own **resolution** (low-res pixel art, native UI, etc.)
+- Has its own **camera**
+- Has its own **layer sorting** rules
+- Has a **presentation mode** (how it maps to the window)
-Parallax
-Layers can have a defined parallax value, set at the engine level. Anything on that layer will move with the provided parallax. Each layer has an implicit parallax value of "1", which means it moves "as expected". Below 1 makes it move slower (0 makes it not move at all), 2 makes it move twice as fast, etc.
+Planes composite in order — later planes draw on top of earlier ones.
-Tilemaps
-These are highly efficient and work just like tilemaps on old consoles. When you submit one of these to draw, Prosperon can efficientally cull what can't be seen by the camera. You can have massive levels with these without any concern for performance. A tilemap is all on its own layer.
+## Layer Sorting
-Tiles can be flipped; and the entire tilemap can have an affine transformation applied to it.
+Within a plane, drawables are sorted by `layer` (integer). Within each layer, you can choose a sorting mode:
-Sprites each have their own layer and affine transform. Tilemaps are just like a large sprite.
-
-In addition to all of this, objects can have a "draw" event, wherein you can issue direct drawing commands like "render.sprite", "render.text", "render.circle", and so on. This can be useful for special effects, like multi draw passes (set stencil -> draw -> revert stencil). In this case, it is the draw event itself with the layer setting.
-
-## 3D
-3d models are like 3d sprites. Add them to the world, and then the engine handles drawing them. If you want special effects, its "draw" command can be overridden.
-
-As sprites and 3d models are sent to render, they are added to a list; sorted; and then finally rendered.
-
-## THE RENDERER
-## Fully scriptable
-
-The render layer is where you do larger scale organizing. For example, for a single outline, you might have an object's draw method be the standard:
-- draw the model, setting stencil
-- draw a scaled up model with a single color
-
-But, since each object doing this won't merge their outlines, you need a larger order solution, wherein you draw *all* models that will be outlined, and then draw *all* scaled up models with a single color. The render graph is how you could do that. The render graph calls draw and render functions; so with a tag system, you can essentially choose to draw whatever you want. You can add new shadow passes; whatever. Of course, prosperon is packed with some standard render graphs to utilize right away.
-
-Each graphical drawing command has a specific pipeline. A pipeline is a static object that defines every rendering detail of a drawing command.
-
-A drawing command is composed of:
- - a model
- - a material
- - a pipeline
-
-The engine handles sorting these and rendering them effectively. There exist helper functions, like "render.image" which will in turn create a material and use the correct model.
-
-You execute a list of drawing commands onto a render target. This might be the computer screen; it might be an offscreen target.
-
-The material's properties are copied into the shader on a given pipeline; they also can have extra properties like "castshadows", "getshadows", and so on.
-
-An *image* is a struct {
- texture: GPU texture
- rect: UV coordinates
+```javascript
+layer_sort: {
+ '0': 'explicit', // engine may reorder for batching efficiency
+ '5': 'y' // sort by Y position (top-down game depth)
}
+```
-## 2D drawing commands
-The 2d drawing commands ultimately interface with a VERY limited subset of backend knowledge, and so are easily adaptable for a wide variety of hardware and screen APIs.
-
-The basic 2D drawing techniques are:
-Sprite - arbitrarily blit a bitmap to the screen with a given affine transformation and color
-Tiles - Uniform squares in a grid pattern, drawn all on a single layer
-Text - Generates whatever is needed to display text wrapped in a particular way at a particular coordinate
-Particles - a higher order construction
-Geometry - programmer called for circles or any other arbitrary shape. Might be slow!
+Y-sorting is essential for top-down games where objects lower on screen should appear in front.
## Effects
-An "effect" is essentially a sequence of render commands. Typically, a sprite draws itself to a screen. It may have a unique pipeline for a special effect. But it might also have an "effect", which is actually a sequence of draw instructions. An example might be an outline scenario, where the sprite draws a black version of it scaled 1.1x, and then draws with the typical pipeline afterwards.
-## A frame
-During a frame, the engine finds everything that needs rendered. This includes enabled models, enabled sprites, tilemaps, etc. This also includes programmer directions inside of the draw() and hud() functions.
+Effects are applied to **groups** of sprites. Tag sprites with group names, then define effects for those groups:
-This high level commands are culled down, accounting for off screen sprites, etc, into a more compact command queue. This command queue is then rendered in whichever way the backend sees fit. Each "command queue" maps roughly into a "render pass" in vulkan. Once you submit a command queue, the data is sorted, required data is uploaded, and a render pass draws it to the specified frame.
+```javascript
+var player = sprite({
+ image: "player.png",
+ groups: ['glow_objects']
+})
-A command is kicked off with a "batch" command.
+var plan = compositor.compile({
+ planes: [{
+ name: 'game',
+ plane: 'default',
+ camera: cam,
+ resolution: {width: 320, height: 240}
+ }],
+ group_effects: {
+ glow_objects: {
+ effects: [
+ {type: 'bloom', threshold: 0.5, intensity: 1.5, blur_passes: 2}
+ ]
+ }
+ }
+})
+```
-var batch = render.batch(target, clearcolor) // target is the target buffer to draw onto
-target must be known when the batch starts because it must ensure the pipelines fed into it are compatible. If clearcolor is undefined, it does not erase what is present on the target before drawing. To disable depth, simply do not include a depth attachment in the target.
+### Available Effects
-batch.draw(mesh, material, pipeline)
-This is the most fundamental draw command you can do. In modern parlance, the pipeline sets up the GPU completely for rendering (stencil, blend, shaders, etc); the material plugs data into the pipeline, via reflection; the mesh determines the geometry that is drawn. A mesh defines everything that's needed to kick of a draw call, including if the buffers are indexed or not, the number of indices to draw, and the first index to draw from.
+**Bloom** — extracts bright areas, blurs them, composites back:
+```javascript
+{type: 'bloom', threshold: 0.8, intensity: 1.0, blur_passes: 2}
+```
-batch.viewport()
+**Mask** — uses sprites in a mask group as a stencil:
+```javascript
+{type: 'mask', mask_group: 'mask_shapes', channel: 'alpha', invert: false}
+```
-batch.sprite
+Sprites in the `mask_group` are not drawn directly — they only serve as the mask shape.
+## Presentation Modes
-batch.text // a text object. faster than doing each letter as a sprite, but less flexible
-// etc
-batch.render(camera)
+Each plane specifies how its render target maps to the window:
-Batches can be saved to be executed again and again. So, one set of batches can be created, and then drawn from many cameras' perspectives. batch.render must take a camera
+| Mode | Behavior |
+|------|----------|
+| `stretch` | Fill the window exactly (may distort aspect ratio) |
+| `letterbox` | Fit inside window, preserving aspect ratio, with black bars |
+| `integer_scale` | Scale by whole numbers only for pixel-perfect rendering |
-Behind the scenes, a batch tries to merge geometry, and does reordering for minimum pipeline changes behind the scenes.
+`integer_scale` automatically uses nearest-neighbor filtering for crisp pixels.
-Each render command can use its own unique pipeline, which entails its own shader, stencil buffer setup, everything. It is extremely flexible. Sprites can have their own pipeline.
+## Execution
-ULTIMATELY:::
-This is a much more functional style than what is typically presented from graphics APIs. Behind the scenes these are all translated to OpenGL or whatever; being functional at this level helps to optimize.
+The compositor produces a **plan** — a list of render passes. The plan is executed by the GPU backend:
-IMPORTANT NOTE:
-Optimization only happens at the object level. If you have two pipelines with the exact same characteristics, they will not be batched. Use the exact same pipeline object to batch.
+```javascript
+var result = compositor.execute(plan)
+// result.commands is sent to the GPU backend
+```
-## SCENARIOS
+In practice, `core.start({render})` handles this for you. Your render callback returns the compositor result and the engine executes it.
-BLOOM BULLETS
-You want to draw a background; some ships; and some bullets that have glow to them. This amounts to two ideas:
-1) draw the background and ships
-2) draw bullets to a texture
-3) apply bloom on the bullet
-4) draw bullets+bloom over the background and ships
+## Manual Drawables
-Steps 1, and 2-3, can be done in parallel. They constitute their own command queues. When both are done, the composite can then happen.
+You can inject drawables that aren't registered with film2d by adding them directly to a plane config:
-var bg_batch = render.batch(surf1, camera);
-bg_batch.draw(background)
-bg_batch.draw(ships)
-bg_batch.end()
+```javascript
+planes: [{
+ name: 'game',
+ plane: 'default',
+ camera: cam,
+ drawables: [
+ {type: 'sprite', image: tex, pos: {x: 0, y: 0}, width: 32, height: 32, layer: 0}
+ ]
+}]
+```
-var bullet_batch = render.batch(surf2, camera);
-bullet_batch.draw(bullets)
-bullet_batch.end()
+These are merged with the registered drawables for that plane.
-var bloom = render.batch(surf3, postcam)
-bloom.draw(bullet_batch.color, bloom_pipeline)
-bloom.end()
+## Render Targets
-var final = render.batch(swapchain)
-final.draw(bg_batch.color)
-final.draw(bloom.color)
-final.end()
+The compositor automatically manages intermediate render targets for effects. You don't need to create or manage GPU textures — the compositor allocates and reuses them as needed.
-When 'batch.end' is called, it reorders as needed, uploads data, and then does a render pass.
+## Debug
-3D GAME WITH DIRECTIONAL LIGHT SHADOW MAP
-
-var shadow_batch = render.batch(shadow_surf, dir_T)
-shadow_batch.draw(scene, depth_mat) // scene returns a list of non culled 3d obejcts; we force it to use depth_mat
-shadow_batch.end()
-
-base_mat.shadowmap = shadow_batch.color;
-
-var main_batch = render.batch(swapchain, camera)
-main_batch.draw(scene)
-main_batch.end()
-
-FIERY LETTERS
-This pseudo code draws a "hello world" cutout, with fire behind it, and then draws the game's sprites over that
-
-var main = render.batch(swapchain, 2dcam)
-main.draw("hello world", undefined, stencil_pipeline)
-main.draw(fire)
-main.draw(fullscreen, undefined, stencil_reset)
-main.draw(game)
-main.end()
+Pass `debug: 'graph'` to `core.start` config to log the compiled render plan as JSON. Pass `debug: 'cmd'` to log the final command list sent to the GPU.
diff --git a/docs/prosperon.md b/docs/prosperon.md
index 54adb8a1..ce53832b 100644
--- a/docs/prosperon.md
+++ b/docs/prosperon.md
@@ -1,9 +1,47 @@
+---
+title: "About Prosperon"
+type: docs
+---
+
# Prosperon
-Prosperon is a game programming environment. It's a descriptive language for making a game, which can have many engines as its backend; it ships with its own.
+Prosperon is a 2D game programming environment built on [Pit](https://crumbpit.org), an actor-based language designed for safe, sandboxed scripting.
-It is primarily 2D focused; think of it like a 2d S&Box. Its aim is to make it easy to make a 2d game that works anywhere.
+Games are written as small, focused modules in Pit — a simplified JavaScript with actor primitives. Prosperon handles rendering, input, audio, and asset management. No class hierarchies, no giant scene trees.
-Key to it is how you use and define sprites.
+## Design Goals
-Prosperon games are written in cell. Cell and prosperon are designed for simple AI usage. An AI can add elements to the game directly, it can code it, etc; cell and prosperon both are designed to have many small files with simple dependencies, which allow LLMs to utilize them easily.
\ No newline at end of file
+- **Minimal API surface.** A handful of modules cover sprites, input, sound, and composition. Each does one thing.
+- **Data-driven rendering.** You create sprites and set properties. The compositor handles batching, sorting, and GPU submission.
+- **Composition over inheritance.** Entity behavior comes from modules, not class trees. An entity type is a script that returns a prototype object.
+- **AI-friendly.** Many small files with simple dependencies. An LLM can read a module, understand it, and modify it.
+- **Text-first.** Assets are referenced as string paths. Levels are JSON. Everything diffs cleanly in source control.
+
+## Architecture
+
+Prosperon is not a monolithic engine with a global state object. It is a collection of modules:
+
+| Module | Purpose |
+|--------|---------|
+| `core` | Main loop, window, GPU init |
+| `sprite` | Create and manage sprites |
+| `compositor` | Build render plans from scene configs |
+| `input` | Action mapping, device routing |
+| `sound` | Audio playback |
+| `world` | Entity management |
+| `camera` | Viewport into the world |
+| `text2d` | Text rendering |
+| `shape2d` | SDF shapes |
+| `tilemap2d` | Grid-based tile rendering |
+| `tween` | Value interpolation |
+| `resources` | Asset path resolution |
+
+Import what you need with `use()`. If you don't use a module, it doesn't load.
+
+## The Language
+
+Pit is an actor-based language. Programs (`.ce` files) are actors that run top-to-bottom, can register message handlers, and spawn child actors. Modules (`.cm` files) return frozen values and are cached.
+
+Actor primitives: `$start`, `$receiver`, `send`, `$delay`, `$clock`, `$stop`.
+
+See [Quickstart](../quickstart/) for a hands-on introduction.
diff --git a/docs/quickstart.md b/docs/quickstart.md
index fd2fe9cd..c8e63f6b 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -1,47 +1,19 @@
+---
+title: "Quickstart"
+type: docs
+---
+
# Quickstart
-This quickstart walks through running Cell programs, exploring examples, and creating your first module and program. It assumes the `./cell` executable is present at the repo root (already built or downloaded).
+This quickstart walks through creating modules, programs, and running examples.
-## 1) Initialize a Project Shop (.cell)
+## Your First Module
-Create the project structure used for module management and bytecode cache:
+A module (`.cm`) returns a frozen value — an API, a config, data.
-- `./cell init`
-
-This creates `.cell/` with `cell.toml`, `lock.toml`, `modules/`, `build/`, and `patches/`.
-
-## 2) Run Built‑in Examples
-
-Examples under `examples/` are programs (`*.ce`) you can run directly:
-
-- NAT portal server (introduction service):
- - `./cell examples/nat`
-- NAT client (contacts a portal):
- - `./cell examples/nat_client`
-- Non‑blocking HTTP download actor:
- - `./cell examples/http_download_actor`
-
-Tip: Use these as references for portals, contacts, and non‑blocking I/O.
-
-## 3) Run the Accio Game
-
-Accio lives under `accio/`. The program is `accio/accio.ce` and supports modes via arguments:
-
-- Start menu: `./cell accio start`
-- Load a level: `./cell accio level game/1.json`
-- Analyze assets: `./cell accio analyze`
-- Clean level data: `./cell accio clean`
-
-Arguments after the program path are available inside the program via `arg` (e.g., `arg[0] == 'start'`).
-
-## 4) Your First Module (.cm) and Program (.ce)
-
-Create a module that returns a frozen API object, and a program that uses it.
-
-- File: `examples/hello.cm` (module)
+Create `hello.cm`:
```javascript
-// Returns a value (frozen by the engine)
return {
greet: function(name) {
return `Hello, ${name}!`
@@ -49,39 +21,43 @@ return {
}
```
-- File: `examples/hello.ce` (program)
+## Your First Program
+
+A program (`.ce`) is an entry point. It runs top-to-bottom and can register handlers.
+
+Create `hello.ce`:
```javascript
-var hello = use('examples/hello')
+var hello = use('hello')
-log.console(hello.greet('Cell'))
+log.console(hello.greet('Prosperon'))
$receiver(function(msg) {
if (msg.type == 'ping') {
- send(msg, {type:'pong'})
+ send(msg, {type: 'pong'})
}
})
$delay(_ => $stop(), 0.1)
```
-Run it:
+## Modules vs Programs
-- `./cell examples/hello`
+| | Modules (`.cm`) | Programs (`.ce`) |
+|---|---|---|
+| Must return a value | Yes | No |
+| Return value frozen | Yes | N/A |
+| Can register handlers | No | Yes (`$receiver`) |
+| Can spawn children | No | Yes (`$start`) |
+| Cached after first load | Yes | N/A |
-Notes:
-- Modules are `*.cm` and must return a value. The engine deep‑freezes return values, so mutate via new objects or closures rather than in‑place.
-- Programs are `*.ce` and must not return a value. They run top‑to‑bottom when spawned and can register handlers via `$receiver()` and schedule work via `$delay()` or `$clock()`.
+## Spawning Actors
-## 5) Spawning Child Programs (Actors)
-
-Programs can spawn other programs and receive lifecycle events.
-
-- File: `examples/spawner.ce`
+Programs can spawn other programs as child actors:
```javascript
$receiver(function(e) {
- if (e.type == 'greet' && e.actor) {
+ if (e.type == 'greet') {
log.console('Child greeted me')
}
})
@@ -90,44 +66,46 @@ $start(function(info) {
if (info.type == 'greet') {
log.console('Spawned child actor')
}
-}, 'examples/hello.ce')
+}, 'hello.ce')
$delay(_ => $stop(), 0.5)
```
-Run it:
+## Actor Primitives
-- `./cell examples/spawner`
+| Primitive | Purpose |
+|-----------|---------|
+| `$start(callback, path)` | Spawn a child actor from a program |
+| `$receiver(callback)` | Register a message handler |
+| `send(target, message)` | Send a message to an actor |
+| `$delay(callback, seconds)` | Schedule a one-shot callback |
+| `$clock(callback, interval)` | Schedule a repeating callback |
+| `$stop()` | Terminate the current actor |
-## 6) Module Shop Basics
+## Module Resolution
-The module shop manages vendored dependencies under `.cell/modules/` and caches compiled bytecode under `.cell/build/`.
+`use('path')` looks for modules in this order:
-Common commands (all are programs under `scripts/`):
+1. The current module's directory for `path.cm`
+2. The mounted roots (the program's directory is mounted) for `path.cm`
+3. Embedded/native modules if no script file is found
-- Initialize (if you haven’t already):
- - `./cell init`
-- See available commands:
- - `./cell help`
-- Configure system/actor settings:
- - `./cell config list`
- - `./cell config set system.reply_timeout 60`
- - `./cell config actor prosperon/_sdl_video set resolution 1280x720`
-- Download or vendor dependencies (from `.cell/cell.toml`):
- - `./cell mod download`
+Modules compile to bytecode (`.o` files) and are cached for fast reloading.
-## 7) How Module Resolution Works
+## Example Games
-- `use('path')` checks:
- 1) The current module’s directory for `path.cm` while loading.
- 2) The mounted roots (the program’s directory is mounted) for `path.cm`.
- 3) Embedded/native modules if no script file is found.
-- Modules compile to `.cell/build/.o` and reuse the cache if newer than the source.
-- Scripted modules can extend embedded modules via prototype; if the script returns nothing, the embedded module is used as‑is.
+Prosperon ships with example games:
-## 8) Next Steps
+- **Pong** — two-player paddle game
+- **Snake** — growing snake on a grid
+- **Tetris** — falling block puzzle
+- **Chess** — full chess with move validation
+- **Bunnymark** — sprite rendering benchmark
-- Language details: `docs/cell.md`
-- Actors, programs, and messaging: `docs/actors.md`
-- Rendering, input, and resources: `docs/rendering.md`, `docs/input.md`, `docs/resources.md`
-- Full API reference: `docs/api/`
+## Next Steps
+
+- [Tutorial](tutorial/) — Build a game step by step
+- [Entities](entities/) — The world and entity model
+- [Rendering](rendering/) — How drawing works
+- [Input](input/) — Handling player input
+- [API Reference](api/) — Full module documentation
diff --git a/docs/rendering.md b/docs/rendering.md
index eb4c6a09..5ee73813 100644
--- a/docs/rendering.md
+++ b/docs/rendering.md
@@ -1,36 +1,113 @@
-# Rendering, the camera & the world
+---
+title: "Rendering"
+type: docs
+---
-Prosperon is rendered via a single camera, defined on `prosperon.camera`. Examine the `camera` object in the API for detail about its properties.
+# Rendering
-The camera has a width and a height, which defines the resolution of the rendered game. The camera also has a transform `prosperon.camera.transform`, which should be used to move it. The camera's position can also be set via `prosperon.camera.pos`.
+Prosperon's rendering is **data-driven**. You create sprites and other drawables, set their properties, and the engine handles drawing them. You never touch the GPU directly.
+
+## The Model
+
+Think of it like programming sprites on a classic console:
+
+1. **Create** a sprite (or text, shape, tilemap, particle emitter)
+2. **Set** its position, image, layer, and visual properties
+3. The engine **draws** everything each frame, sorted and batched automatically
+
+```javascript
+var sprite = use('sprite')
+
+var player = sprite({
+ image: "player.png",
+ pos: {x: 100, y: 200},
+ layer: 5
+})
+
+// Move it later
+player.pos.x = 150
+player.pos.y = 250
+```
+
+That's it. The sprite is registered with the engine when created and drawn every frame until destroyed.
## Coordinate System
-There are two important coordinate systems in Prosperon: world and hud. Both coordinate systems are measured in pixels, with X+ going to the right, and Y+ going up.
-**World space** is the game world, and is what the camera "sees". The center of the camera is the coordinate it is viewing; so, for example, if the camera is at position [100,100], the pixel at the center of the screen is whatever pixel is at world space [100,100].
+There are two coordinate spaces:
-**Hud space** can be thought of as the screen itself, or the lens of the camera. The bottom left of the viewing window is [0,0], and the top right is [camera.width, camera.height].
+**World space** is the game world. The camera's position determines what's visible. If the camera is at `[100, 100]`, that world coordinate is at the center of the screen. X+ goes right, Y+ goes up.
-## The render module & queues
-Internally, prosperon keeps a rendering queue as draw commands are issued throughout the frame. It keeps two: 'draw' and 'hud'. At the end of the frame, draw commands are sorted and issued to the GPU: first the 'draw' queue, and then the 'hud' queue, so that it overlays.
+**HUD space** is the screen itself. `[0, 0]` is the bottom-left corner, `[camera.width, camera.height]` is the top-right. HUD elements don't move with the camera.
-The render module contains functionality for low level rendering. It includes commands like 'scissor' and 'viewport', it is more recommended to use the 'draw2d' module to do the majority of the drawing.
+## Layers
-## Draw2d
-The easiest way to draw with prosperon is to use the 'draw2d' module. Prosperon defines two events via `prosperon.on`: `draw` and `hud`. During a frame, the `render` module sets its internal queue to `draw`, and then the `draw` event is issued. Then it does the same for `hud`. The 'draw2d' module is a high level way to issue drawing commands to the current queue. So, a simple example:
+Every drawable has a `layer` — an integer that determines draw order. Lower layers draw first (behind), higher layers draw last (in front).
-```
-var draw = use('draw2d')
-// main.js
-var rect = {x:0,y:0,width:100,height:100} // a 100x100 square at [0,0]
-this.draw = function() {
- draw.rectangle(rect) // this will draw with the bottom left corner at the center of the screen
-}
+Within a layer, objects can be sorted by different criteria:
-this.hud = function() {
- draw.rectangle(rect) // this draws a rectangle at the botomm left corner of the screen
-}
+- **explicit** (default) — engine may reorder for performance
+- **y-sort** — sort by Y position, useful for top-down games where "lower" objects appear in front
+
+## Planes
+
+Drawables are organized into **planes**. A plane is a named group that can have its own resolution, camera, and effects. By default, everything goes into the `'default'` plane.
+
+Planes render independently and composite onto the screen. This lets you have a game world at 320x240 pixel-art resolution and a HUD at native resolution, for example.
+
+## Effects
+
+Planes and groups of sprites can have effects applied:
+
+- **Bloom** — threshold + multi-pass Gaussian blur
+- **Mask** — stencil masking using other sprites as the mask shape
+
+Effects are declared in the compositor config and applied automatically. You tag sprites with groups, then apply effects to those groups.
+
+## Presentation Modes
+
+The final composited frame is presented to the window in one of three modes:
+
+| Mode | Behavior |
+|------|----------|
+| `stretch` | Fill the window, may distort |
+| `letterbox` | Fit inside window, preserve aspect ratio, black bars |
+| `integer_scale` | Scale by whole numbers only, nearest-neighbor, pixel-perfect |
+
+## Camera
+
+A camera defines the viewport into the world:
+
+```javascript
+var camera = use('camera')
+var cam = camera.make({
+ pos: {x: 0, y: 0},
+ width: 320,
+ height: 240
+})
```
-## Imgui
-Prosperon includes imgui. Another event is the `imgui` event, where you can issue imguie commands via the 'imgui' module. Examine the 'imgui' API for more info.
+The camera's `width` and `height` set the game's internal resolution. The `pos` determines what world coordinate is at the center of the screen.
+
+## Drawable Types
+
+| Type | Module | Description |
+|------|--------|-------------|
+| Sprite | `sprite` | Textured quad with transform, tint, UV mapping |
+| Text | `text2d` | Rendered text with font, size, wrapping |
+| Shape | `shape2d` | SDF-rendered rectangles, circles, ellipses, pills |
+| Tilemap | `tilemap2d` | Grid of tiles on a single layer |
+| Particles | `particles2d` | Particle emitter producing sprite-like particles |
+| Line | `line2d` | Polylines and line segments as triangle meshes |
+
+All drawable types share common properties: `pos`, `layer`, `plane`, `groups`, `visible`, `opacity`, `tint`.
+
+## How It Works Internally
+
+For those curious about the pipeline (you don't need to know this to use Prosperon):
+
+1. Drawables register with `film2d` — the internal sprite registry
+2. Each frame, the **compositor** queries film2d for all visible drawables
+3. Drawables are sorted by layer, batched by texture/material
+4. Effect groups are rendered to intermediate targets, effects applied
+5. Planes composite to the screen via the **SDL GPU backend**
+6. The backend uses Metal, Vulkan, or DirectX depending on platform
diff --git a/docs/resources.md b/docs/resources.md
index 29a63373..e038cef6 100644
--- a/docs/resources.md
+++ b/docs/resources.md
@@ -1,17 +1,48 @@
-# Resources & Data
+---
+title: "Resources"
+type: docs
+---
-Prosperon, being text based, encourages you to reference assets - images, sounds, etc - as strings.
+# Resources
-## Module Loading and Discovery
+Prosperon uses string paths to reference assets — images, sounds, fonts, scripts. The resource system resolves these paths and caches loaded assets.
-Prosperon uses a set of **search paths** to locate modules. Each path in this list is consulted in order, and the first matching module file is loaded.
-**prosperon.PATH** is an array which is a list of directories in which it looks for resources. Developers can add or remove entries from this list, controlling where the engine will search. Assets are searched from `prosperon.PATH[0]` first.
+## Module Resolution
-**Extension Matching:** Assets should be referenced without a file extension. Based on the context of what asset is being searched for, a list of file extensions are iterated through to find the asset. When setting a sprite's texture, for example, first a 'png' may be searched for, then a 'gif', and so on. If a specific extension is requested, that specific extension - and only that one - will be searched for. it's very useful to not specify extensions at all, because that enables prosperon to optimize assets and replace them with backend specific ones, and the game will still work.
+`use('path')` loads a module by searching for `path.cm` in this order:
-Prosperon **caches** each loaded assets. When a sprite references a 'png', if another one does, it uses the same GPU texture.
+1. The current module's directory
+2. Mounted roots (the program's directory is always mounted)
+3. Embedded/native modules if no script file is found
+
+Modules compile to bytecode (`.o` files) and are cached after first load. Subsequent `use()` calls return the cached value.
+
+## Asset Resolution
+
+When you reference an asset like `"player.png"`, the resource system searches mounted paths for a match. Assets should generally be referenced **without** a file extension — the engine tries appropriate extensions based on context:
+
+- **Images:** png, qoi, gif, jpg, jpeg, ase
+- **Sounds:** wav, ogg, mp3
+- **Fonts:** ttf
+
+Omitting extensions lets the engine swap optimized formats transparently. If you specify an extension, only that exact file is searched for.
+
+## Caching
+
+All loaded assets are cached. When two sprites reference the same image path, they share one GPU texture. Sound data, fonts, and bytecode are similarly deduplicated.
## Mounts
-Prosperon uses the idea of file mounting for all io operations. To start with, the write directory is set to the folder prosperon is ran from. That same folder is also mounted as a read directory, as well as the executable itself, which contains a zip file of the engine's core assets.
-Folders or archives like zip records can be mounted, allowing for an easy way to overwrite specific files with new ones. Because prosperon deals with file systems and strings for its operations, this makes modding easy. For example, a mod `mod.zip` may have a file `sprites/bug.png`. Once mounted, when your game loads `bug` as a sprite, that would be grabbed instead of your game's natives `bug`.
+Prosperon uses a virtual filesystem. The write directory is set to the folder the engine runs from. That same folder is mounted as a read directory, along with the executable itself (which contains a zip archive of core engine assets).
+
+Additional folders or zip archives can be mounted, allowing mods to override specific files:
+
+```javascript
+// A mod zip containing sprites/bug.png will override the game's bug.png
+```
+
+Because all asset references are string paths resolved through the mount system, modding is straightforward — mount a new archive and its files take priority.
+
+## .prosperonignore
+
+A `.prosperonignore` file in a mounted directory excludes files from resolution, similar to `.gitignore`. This is useful for keeping source assets (e.g., Aseprite working files) out of the build.
diff --git a/docs/tutorial.md b/docs/tutorial.md
index cc9a2d4b..4657a9eb 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -1,67 +1,225 @@
+---
+title: "Tutorial"
+type: docs
+---
+
# A Tutorial Introduction
-Let's make your first game. When prosperon is launched, it looks in the folder it's in for a ```config.js``` and ```main.js```. These are your first two examples of a module and a program.
+This tutorial walks through building a simple game from scratch. By the end, you'll understand modules, programs, rendering, and input.
-A module is a file that returns a single value. It could be any value: a number, a function, a string. Usually, it's an object. Values are **frozen** before returning, so they cannot be modified. A module is ran once and cached, so any program that accesses a module subsequently is simply getting the already cached object. Modules can import other modules. Circular references are not allowed.
+## How Prosperon Starts
-The ```config.js``` module must return a single object that describes your game. It sets up the window name, starting size, etc. Below is the default config, demonstrating all of the values that can be set on it.
+Prosperon runs a `.ce` program file as the root actor. This is your game's entry point. There is no `config.js` or `main.js` — everything is modules you import with `use()`.
-| Key | Default Value | Type | Description |
-|--------------------|----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------|
-| **title** | `Prosperon [\${prosperon.version}-\${prosperon.revision}]` | string | Title of the game window, typically including version information. |
-| **width** | `1280` | number | Initial width of the game window. |
-| **height** | `720` | number | Initial height of the game window. |
-| **icon** | `graphics.make_texture(io.slurp('icons/moon.gif'))` | object | Icon texture for the game window, loaded from the provided file. |
-| **high_dpi** | `0` | number | Enables (1) or disables (0) High DPI support for the window. |
-| **alpha** | `1` | number | Alpha channel setting for the window (0 to disable, 1 to enable transparency). |
-| **fullscreen** | `0` | number | Sets whether the window should launch in fullscreen (1) or windowed (0). |
-| **sample_count** | `1` | number | Multisampling count for rendering. Increasing this can improve image quality at the cost of performance. |
-| **enable_clipboard** | `true` | boolean | Enables clipboard functionality within the application. |
-| **enable_dragndrop** | `true` | boolean | Enables drag-and-drop functionality for files into the window. |
-| **max_dropped_files** | `1` | number | Maximum number of files that can be dropped into the window at once. |
-| **swap_interval** | `1` | number | Controls vertical synchronization (VSync). Commonly 1 for enabling or 0 for disabling VSync. |
-| **name** | `"Prosperon"` | string | Human-readable name of the application. |
-| **version** | `prosperon.version + "-" + prosperon.revision` | string | Version string for the application, dynamically built from Prosperon's version info. |
-| **identifier** | `"world.pockle.prosperon"` | string | Package or bundle identifier for your game/application. |
-| **creator** | `"Pockle World LLC"` | string | Name of the creator or organization behind the application. |
-| **copyright** | `"Copyright Pockle World 2025"` | string | Copyright declaration for the application. |
-| **type** | `"application"` | string | The general content type or category of this project. |
-| **url** | `"https://github.com/johnbrethauer/prosperon"` | string | URL link associated with the project, such as a repository or official homepage. |
+## Step 1: A Window
+Create `game.ce`:
-With the engine configured, prosperon starts ```main.js``` as the first **actor** of the game. Actors are created from files that **do not** return a value. An actor executes the statements in its script. Initialization should happen here. An actor can pull in other chunks of code by importing modules. Modules are imported with the ```use``` statement. ```use``` returns the value the module returned, and it can be assigned to any variable. To use the internal ```io``` module, for example, you might say ```var io = use('cellfs')```.
-
-Actors exist until they are explicitly killed, by invoking their ```kill``` function. Because the actor created from ```main.js``` is the root actor for the entire game, when it is killed, the program exits. In an actor script file, ```this``` is set to the actor being spawned from the script.
-
-## Our first program
-With that out of the way, we can establish our first simple program.
-
-In the folder with your ```prosperon``` executable, create a ```config.js``` and set it to be the following:
+```javascript
+var core = use('core')
+core.start({
+ title: "My Game",
+ width: 1280,
+ height: 720
+})
```
+
+Run it with `prosperon game.ce`. You get a window. `core.start()` initializes the GPU backend, opens the window, and starts the main loop.
+
+## Step 2: Drawing a Sprite
+
+Sprites are the fundamental drawable. Create one and it appears on screen — you don't issue draw calls.
+
+```javascript
+var core = use('core')
+var sprite = use('sprite')
+var compositor = use('compositor')
+var camera = use('camera')
+
+var cam = camera.make({
+ pos: {x: 0, y: 0},
+ width: 320,
+ height: 240
+})
+
+var player = sprite({
+ image: "player.png",
+ pos: {x: 0, y: 0},
+ width: 32,
+ layer: 5
+})
+
+var plan = compositor.compile({
+ planes: [{
+ name: 'game',
+ plane: 'default',
+ resolution: {width: 320, height: 240},
+ camera: cam,
+ presentation: 'integer_scale'
+ }]
+})
+
+core.start({
+ title: "Sprite Test",
+ width: 1280,
+ height: 720,
+ render: function() {
+ return compositor.execute(plan)
+ }
+})
+```
+
+Key ideas:
+- `sprite()` creates a sprite and registers it with the engine automatically
+- The **compositor** describes how to render the scene: which planes, at what resolution, with what camera
+- Your `render` callback returns the compositor result — the engine sends it to the GPU
+
+## Step 3: Moving the Sprite
+
+Add an `update` callback to move things each frame:
+
+```javascript
+core.start({
+ title: "Moving Sprite",
+ width: 1280,
+ height: 720,
+ update: function(dt) {
+ player.pos.x += 60 * dt
+ },
+ render: function() {
+ return compositor.execute(plan)
+ }
+})
+```
+
+`dt` is the time elapsed since the last frame, in seconds. Multiply speeds by `dt` for frame-rate-independent movement.
+
+## Step 4: Handling Input
+
+Use the `input` module to map physical keys to named actions, then route those actions to your game objects:
+
+```javascript
+var input = use('input')
+
+input.configure({
+ action_map: {
+ move_left: ['a', 'left'],
+ move_right: ['d', 'right'],
+ jump: ['space']
+ }
+})
+
+var player_entity = {
+ on_input: function(action, data) {
+ if (action == 'move_left' && data.pressed) {
+ player.pos.x -= 16
+ }
+ if (action == 'move_right' && data.pressed) {
+ player.pos.x += 16
+ }
+ }
+}
+
+var p1 = input.player1()
+p1.possess(player_entity)
+```
+
+The input system handles keyboard, gamepad, and touch. You define actions once and bind them to any physical input.
+
+## Step 5: Adding Sound
+
+```javascript
+var sound = use('sound')
+
+sound.play("jump.wav")
+```
+
+Sounds are loaded by path. The engine caches audio data so loading the same sound twice reuses the existing data.
+
+## Step 6: Multiple Planes
+
+Real games often need separate rendering layers — pixel art at low res, HUD at native res:
+
+```javascript
+var plan = compositor.compile({
+ clear: {r: 0.1, g: 0.1, b: 0.2, a: 1},
+ planes: [
+ {
+ name: 'game',
+ plane: 'default',
+ resolution: {width: 320, height: 240},
+ camera: cam,
+ layer_sort: {'5': 'y'},
+ presentation: 'integer_scale'
+ },
+ {
+ name: 'hud',
+ plane: 'hud',
+ resolution: {width: 1280, height: 720},
+ camera: hud_cam,
+ presentation: 'stretch'
+ }
+ ]
+})
+```
+
+Sprites belong to a plane via their `plane` property. Each plane renders at its own resolution with its own camera, then composites onto the screen in order.
+
+## Step 7: Entities
+
+For anything more than a few sprites, use the entity system. Define entity types as modules, create instances with overrides:
+
+```javascript
+// entities/coin.cm
+var sprite = use('sprite')
+
return {
- title: "Hello World",
- width: 500,
- height:500
+ value: 1,
+ image: "coin.png",
+
+ init: function() {
+ this.sprite = sprite({
+ image: this.image,
+ pos: this.pos,
+ width: 16,
+ layer: 3
+ })
+ },
+
+ on_destroy: function() {
+ this.sprite.destroy()
+ }
}
```
-This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
+```javascript
+// In your game program
+var world = use('world')
+var coin_proto = use('entities/coin')
-```
-log.console("Hello world")
-
-this.delay(_ => {
- this.kill();
-}, 3)
+var c = world.add_entity(coin_proto, {
+ pos: {x: 50, y: 80},
+ value: 5
+})
```
-**delay** is a function on all actors in prosperon. It executes a given function after a number of seconds. In this case, after 3 seconds, the root actor kills itself, closing the window.
+The entity's `init()` runs after overrides are applied. The prototype defines every valid field with a default — it is the schema.
-## The prosperon global
-The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
+## Putting It Together
-## Getting help
-The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
+A complete minimal game has:
-Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
\ No newline at end of file
+1. A `.ce` program that imports modules and calls `core.start()`
+2. Sprite and entity modules (`.cm`) that define game objects
+3. A compositor config that describes the rendering setup
+4. Input configuration that maps keys to actions
+
+The engine does the rest: batching sprites, sorting layers, managing GPU resources, polling events, scheduling frames.
+
+## Next Steps
+
+- [Graphics](../graphics/) — Sprites, text, shapes, tilemaps
+- [Compositor](../ops/) — Planes, effects, presentation modes
+- [Input](../input/) — Actions, players, control stacks
+- [Entities](../entities/) — The entity model and world system
diff --git a/website/.gitignore b/website/.gitignore
new file mode 100644
index 00000000..8dd6d56b
--- /dev/null
+++ b/website/.gitignore
@@ -0,0 +1,2 @@
+public/
+.hugo_build.lock
diff --git a/website/content/_index.html b/website/content/_index.html
new file mode 100644
index 00000000..a4a4e8f6
--- /dev/null
+++ b/website/content/_index.html
@@ -0,0 +1,165 @@
+---
+title: "Prosperon Engine"
+---
+
+
+
+
+
+ A minimal, fast, and flexible 2D game engine built on Pit—an actor-based language designed for safe, sandboxed scripting. Prosperon gives you a compositor-driven rendering pipeline, multi-device input, and reflective tooling, all in a tiny executable you version with your project.
+
+
+
+
+
+
What Is Prosperon?
+
+ Prosperon is a game programming environment for 2D games. Think of it like a 2D S&Box: you write game logic in Pit (a simplified, sandboxed JavaScript), and Prosperon handles rendering, input, audio, and asset management. Games are written as small, focused modules that compose together—no class hierarchies, no giant scene trees.
+
+
+ The engine ships as a single executable. Drop it in a folder, write your scripts, and run. Your entire project is text files—friendly to source control, easy for collaborators, and readable by AI tools.
+
+
+
+
+
+
Key Features
+
+
+
Compositor Rendering
+
Describe your scene as planes, layers, and effects. The compositor builds a render plan—bloom, masks, shader passes—and the SDL GPU backend executes it. You declare what to draw; the engine decides how.
+
+
+
Duck-Typed Sprites
+
If an object looks like a sprite, it is a sprite. Register any object with film2d and it draws. No base classes, no component systems to configure. Just data.
+
+
+
Actor-Based Scripting
+
Game logic runs in Pit—an actor language where each program has its own heap and message queue. Spawn actors, send messages, schedule work with $delay and $clock. Clean isolation without threads.
+
+
+
Multi-Device Input
+
Keyboard, mouse, gamepad, and touch—normalized into named actions. Supports user pairing, control stacks (possess/push/pop), emacs-style keybindings, and gesture recognition out of the box.
+
+
+
Source-Control Friendly
+
Every game file is text. Modules are .cm files, programs are .ce files, configs are plain objects. No binary scene files until you ship. Git merges just work.
+
+
+
Tiny & Self-Contained
+
Prosperon is a single executable. No installer, no SDK, no build tools. Version it alongside your project. Supports Windows, Mac, and Linux.
+
+
+
+
+
+
+
How It Works
+
A Prosperon game is a collection of modules (.cm) and programs (.ce). Modules return frozen values—APIs, configs, data. Programs are entry points that run top-to-bottom and can spawn child actors.
+
+
The engine provides core modules for everything you need:
+
+
core — main loop, window management, frame scheduling
tween — fire-and-forget animation with easing functions
+
+
+
You compose these into your game. A typical frame:
+
// Your game registers sprites with film2d
+// The compositor queries film2d for drawables
+// It builds render passes (clear, draw, effects, blit)
+// The GPU backend executes the command list
+// Input events are normalized and dispatched to your pawns
+// Audio pumps in the background via $delay
+
+
+
+
+
The Rendering Pipeline
+
Prosperon's rendering is data-driven. Your game logic never touches the GPU. Instead:
+
+
Register drawables (sprites, shapes, text) with film2d
+
Describe your scene as planes with optional effects (bloom, masks)
+
The compositor queries film2d, sorts by layer, builds render passes
+
The SDL GPU backend executes the command list on Metal/Vulkan/DirectX
+
Final output is blitted to screen with letterbox, integer scale, or stretch
+
+
+ This means you can inspect the entire render plan as data. Debug it, log it, swap backends. The renderer supports sprite batching, y-sorting, effect pipelines (threshold + blur for bloom, mask rendering), and multiple presentation modes for pixel-perfect scaling.
+
+
+
+
+
+
The Language: Pit
+
+ Prosperon games are written in Pit, an actor-based language that looks like simplified JavaScript. Pit is designed for safe, sandboxed execution—each actor runs in its own heap with hard memory and time limits.
+
+ Read the Prosperon manual to learn about the module system, rendering pipeline, input handling, and deploying your game. Prosperon also ships with example games (pong, snake, chess, tetris, bunnymark) to help you learn fast.
+
+
+
+
+
+
License
+
+ All Prosperon licenses are commercial. You can distribute Prosperon-based games with no royalties and freely ship the Prosperon runtime. For full details, visit the license page.
+
+
diff --git a/website/content/changelog/_index.md b/website/content/changelog/_index.md
new file mode 100644
index 00000000..0aec177f
--- /dev/null
+++ b/website/content/changelog/_index.md
@@ -0,0 +1,7 @@
+---
+title: "Changelog"
+---
+
+Release notes are published on the [Gitea repository](https://gitea.pockle.world/john/prosperon/releases).
+
+Check back here for updates as new versions are released.
diff --git a/website/content/docs/_index.md b/website/content/docs/_index.md
new file mode 100644
index 00000000..c2c721c3
--- /dev/null
+++ b/website/content/docs/_index.md
@@ -0,0 +1,27 @@
+---
+title: "Prosperon Manual"
+type: "docs"
+---
+
+Welcome to the Prosperon documentation. This manual covers everything from getting started to the full API reference.
+
+## Getting Started
+
+- [Quickstart](/docs/quickstart/) — Modules, programs, and actor primitives
+- [Tutorial](/docs/tutorial/) — Build your first game step by step
+
+## Concepts
+
+- [Rendering](/docs/rendering/) — The data-driven rendering model
+- [Graphics](/docs/graphics/) — Sprites, text, shapes, tilemaps, particles
+- [Compositor](/docs/ops/) — Planes, effects, and presentation modes
+- [Entities](/docs/entities/) — The script + overrides entity model
+- [Input](/docs/input/) — Actions, players, and control stacks
+- [Resources](/docs/resources/) — Asset loading, discovery, and caching
+- [About Prosperon](/docs/prosperon/) — Architecture and design goals
+
+## API Reference
+
+- [API Index](/docs/api/) — All modules, primitives, and types
+
+Browse the full reference in the sidebar.
diff --git a/website/content/features/_index.md b/website/content/features/_index.md
new file mode 100644
index 00000000..89a1a153
--- /dev/null
+++ b/website/content/features/_index.md
@@ -0,0 +1,81 @@
+---
+title: "Features"
+---
+
+## Compositor-Driven Rendering
+
+Prosperon separates *what to draw* from *how to draw it*. Your game registers sprites, shapes, text, tilemaps, and particles with `film2d`. At render time, the **compositor** queries these drawables, organizes them into planes and layers, applies effects, and produces a command list that the GPU backend executes.
+
+This means:
+- **Automatic batching** — sprites sharing a texture and material are drawn together
+- **Layer sorting** — explicit ordering, y-sorting, or engine-optimized ordering per layer
+- **Effects pipeline** — bloom (threshold + multi-pass blur), masks, custom shader passes
+- **Presentation modes** — letterbox, integer scale, or stretch to window
+- **Inspectable** — the render plan is plain data you can log and debug
+
+## Duck-Typed Everything
+
+Prosperon's API uses duck typing throughout. If an object has `type: 'sprite'`, a position, and an image, it's a valid sprite. No base classes to inherit from, no interfaces to implement.
+
+This extends to the entire engine: anything that "looks like" a camera can be used as one. Anything with an `on_input` method can be possessed by a player. This makes prototyping extremely fast — you can test ideas with plain objects before committing to any structure.
+
+## Actor-Based Game Logic
+
+Games are written in [Pit](https://crumbpit.org), an actor language where:
+- Each **program** (`.ce`) runs as an actor with its own heap
+- Actors communicate via **message passing** (`send`, `$receiver`)
+- Work is scheduled with `$delay` (one-shot) and `$clock` (periodic)
+- Actors spawn children with `$start` and terminate with `$stop`
+- **Modules** (`.cm`) return frozen values — shared, immutable APIs
+
+This gives you concurrency without threads, isolation without overhead, and a natural way to structure game systems.
+
+## Multi-Device Input
+
+The input system normalizes keyboard, mouse, gamepad, and touch into **named actions**. A default action map handles common UI actions (up/down/confirm/cancel), and you define game-specific actions on top.
+
+Features:
+- **User pairing** — automatically pair devices to players, or explicit assignment
+- **Control stacks** — possess entities, push/pop UI layers, block input propagation
+- **Emacs-style keybindings** — `C-a` for ctrl+a, `M-x` for alt+x
+- **Gesture recognition** — swipe detection on touch devices
+- **Device-aware icons** — show the right button prompts for the current input device
+
+## Asset Management
+
+Prosperon finds and loads assets by name. Ask for `"slime"` and it searches the path for `slime.png`, `slime.qoi`, `slime.ase`, etc. Supported formats:
+
+| Category | Formats |
+|----------|---------|
+| Images | PNG, QOI, GIF, JPG, Aseprite |
+| Audio | WAV, FLAC, MP3, QOA |
+| Fonts | TTF |
+
+Assets are cached after first load. The virtual filesystem (PhysFS) supports mounting ZIP archives for mods and DLC.
+
+## Audio
+
+Simple audio playback with PCM caching and voice mixing:
+
+```javascript
+var audio = use('sound')
+var voice = audio.play("explosion.wav")
+voice.vol = 0.5
+voice.loop = true
+voice.stopped = true // stop it
+```
+
+Decodes to a uniform sample rate on load. Multiple voices mix independently.
+
+## Text-Based Everything
+
+Prosperon projects are entirely text:
+- Game logic: `.cm` and `.ce` script files
+- Config: plain JavaScript objects
+- No binary scene files until you ship
+
+This means git diffs are readable, merges are clean, and AI tools can understand your entire project.
+
+## Tiny Footprint
+
+Prosperon is a single executable. No installer, no SDK, no 2GB download. Drop it in your project folder and run. Version it alongside your code. Build on any machine with the same binary.
diff --git a/website/content/license.md b/website/content/license.md
new file mode 100644
index 00000000..9c3c6f11
--- /dev/null
+++ b/website/content/license.md
@@ -0,0 +1,39 @@
+---
+title: "Prosperon Commercial License"
+---
+
+**Last Updated:** February 11, 2025
+
+1. **Grant of License**
+ Upon purchasing a Prosperon license, Pockle World LLC (*"Licensor"*) grants you (*"Licensee"*) a non-exclusive, worldwide, commercial license to use the Prosperon game engine (the *"Software"*) to develop, publish, and distribute one or more video game or interactive software products (the *"Products"*).
+
+2. **Closed-Source Restriction**
+ The Software is currently closed-source. Licensee shall not redistribute or disclose the Prosperon engine source code, in whole or in part, unless expressly permitted by Pockle World LLC.
+
+3. **Permitted Usage**
+ Licensee may create and distribute compiled or packaged Products that incorporate or depend upon the Software, for commercial or non-commercial purposes, without owing royalties or additional fees to Licensor (beyond the initial purchase).
+
+4. **Ownership and Copyright**
+ The Software is owned by Pockle World LLC. All rights not expressly granted in this License are reserved by Pockle World LLC.
+
+5. **Modifications**
+ You may modify the scripts of your own Projects, but you may not share or distribute any modifications to the Prosperon engine's proprietary binaries, libraries, or source code itself.
+
+6. **No Warranty**
+ THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+
+7. **Limitation of Liability**
+ IN NO EVENT SHALL LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
+
+8. **Term and Termination**
+ This License shall remain in effect perpetually, unless you fail to comply with any term of this License. In such event, your rights under this License will terminate immediately without notice from Pockle World LLC.
+
+9. **Support and Updates**
+ Purchase of the Software includes access to all updates through Prosperon v1.X. Additional support plans may be purchased separately.
+
+10. **Governing Law**
+ This License shall be governed by and construed in accordance with the laws of the jurisdiction in which Pockle World LLC is located, without regard to conflict-of-law principles.
+
+---
+
+**Contact:** For any questions about this License, please contact Pockle World LLC at [support@pockle.world](mailto:support@pockle.world).
diff --git a/website/content/privacy.md b/website/content/privacy.md
new file mode 100644
index 00000000..9de7988f
--- /dev/null
+++ b/website/content/privacy.md
@@ -0,0 +1,43 @@
+---
+title: "Privacy Policy"
+---
+
+**Last Updated:** February 11, 2025
+
+This Privacy Policy explains how Pockle World LLC ("we," "our," or "us") collects, uses, and discloses your personal information in relation to the Prosperon game engine and its associated websites, forums, and services (collectively, the "Services").
+
+## 1. Information We Collect
+
+- **Personal Data:** We may collect your name, email address, and/or payment details when you purchase a Prosperon license or sign up for our support subscriptions.
+- **Usage Data:** We may gather anonymized analytics data about feature usage, engine error logs, or user interactions to help improve Prosperon.
+
+## 2. How We Use Your Information
+
+- To process license purchases, manage subscriptions, and provide customer support.
+- To communicate product updates, new features, or important changes.
+- To improve our Services through analytics and usage metrics.
+
+## 3. Sharing Your Information
+
+- We do not sell your personal information to third parties.
+- We may share essential data with payment processors or partners who facilitate license fulfillment, under strict confidentiality obligations.
+
+## 4. Data Security
+
+We employ industry-standard measures to protect your information. However, no data transmission or storage can be guaranteed 100% secure.
+
+## 5. Retention
+
+We retain personal data only as long as necessary for the purposes outlined in this Policy or as required by law.
+
+## 6. International Transfers
+
+Your information may be transferred to and stored in servers outside your country of residence. We take steps to ensure compliance with relevant data protection laws.
+
+## 7. Changes to This Policy
+
+We may occasionally update this Privacy Policy to reflect changes in our practices. The updated version will be posted on this page with a revised "Last Updated" date.
+
+## 8. Contact Us
+
+If you have any questions or concerns about this Privacy Policy, please contact us at [support@pockle.world](mailto:support@pockle.world).
diff --git a/website/content/start/_index.md b/website/content/start/_index.md
new file mode 100644
index 00000000..d14216d3
--- /dev/null
+++ b/website/content/start/_index.md
@@ -0,0 +1,108 @@
+---
+title: "Get Started"
+---
+
+## Installation
+
+Grab the Prosperon executable for your platform and drop it in a project folder. Prosperon is a single binary — no installer required.
+
+## Create a Project
+
+Initialize the module system:
+
+```
+./cell init
+```
+
+This creates `.cell/` with the module shop, bytecode cache, and configuration.
+
+## Your First Module
+
+Create `hello.cm` — a module that returns a frozen API:
+
+```javascript
+return {
+ greet: function(name) {
+ return `Hello, ${name}!`
+ }
+}
+```
+
+## Your First Program
+
+Create `hello.ce` — a program that uses the module:
+
+```javascript
+var hello = use('hello')
+log.console(hello.greet('Prosperon'))
+$delay(_ => $stop(), 0.1)
+```
+
+Run it:
+
+```
+./cell hello
+```
+
+## A Simple Game
+
+Create `config.cm` for window settings:
+
+```javascript
+return {
+ title: "My Game",
+ width: 1280,
+ height: 720
+}
+```
+
+Create `main.ce` as your entry point:
+
+```javascript
+var core = use('prosperon/core')
+var sprite = use('sprite')
+var input = use('input')
+
+// Create a sprite
+var player = sprite({
+ image: "player.png",
+ pos: {x: 100, y: 100},
+ width: 32,
+ height: 32
+})
+
+core.start({
+ width: 1280,
+ height: 720,
+ title: "My Game",
+ update: function(dt) {
+ // Game logic here
+ },
+ render: function() {
+ // The compositor handles rendering registered sprites
+ }
+})
+```
+
+## Example Games
+
+Prosperon ships with several examples:
+
+- **Pong** — classic two-player pong
+- **Snake** — snake game with growing tail
+- **Tetris** — falling block puzzle
+- **Chess** — full chess with move validation
+- **Bunnymark** — sprite rendering benchmark
+
+Run any example:
+
+```
+./cell examples/pong
+```
+
+## Next Steps
+
+- [Read the manual](/docs/) for the full API reference
+- [Rendering guide](/docs/rendering/) to understand the compositor
+- [Input guide](/docs/input/) for multi-device input handling
+- [Join the forum](https://discourse.prosperon.dev) to connect with other developers
diff --git a/website/deploy.sh b/website/deploy.sh
new file mode 100755
index 00000000..a3553043
--- /dev/null
+++ b/website/deploy.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+cd "$(dirname "$0")"
+hugo --gc --minify
diff --git a/website/hugo.toml b/website/hugo.toml
new file mode 100644
index 00000000..1228663d
--- /dev/null
+++ b/website/hugo.toml
@@ -0,0 +1,58 @@
+baseURL = 'https://prosperon.dev/'
+languageCode = 'en-us'
+title = 'Prosperon Engine'
+theme = 'prosperon'
+
+[markup]
+ [markup.highlight]
+ noClasses = false
+ style = 'monokailight'
+ [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
+
+[menus]
+ [[menus.main]]
+ name = 'Features'
+ pageRef = '/features/'
+ weight = 10
+ [[menus.main]]
+ name = 'Docs'
+ pageRef = '/docs/'
+ weight = 20
+ [[menus.main]]
+ name = 'Get Started'
+ pageRef = '/start/'
+ weight = 30
+ [[menus.main]]
+ name = 'Changelog'
+ pageRef = '/changelog/'
+ weight = 40
+ [[menus.main]]
+ name = 'Forum'
+ url = 'https://discourse.prosperon.dev'
+ weight = 50
+ [menus.main.params]
+ external = true
+
+[module]
+ [[module.mounts]]
+ source = "content"
+ target = "content"
+ [[module.mounts]]
+ source = "../docs"
+ target = "content/docs"
+ excludeFiles = "index.md"
+
+[cascade]
+ [cascade._target]
+ path = '/docs/**'
+ [cascade.params]
+ type = 'docs'
+
+[params]
+ description = 'A minimal, fast, and flexible 2D game engine built on Pit. Rapid iteration, reflective tooling, and a compositor-driven rendering pipeline.'
+ email = 'press@pockle.world'
+ twitter = 'pockleworld'
+ forum = 'https://discourse.prosperon.dev'
+ gitea = 'https://gitea.pockle.world'
diff --git a/website/themes/prosperon/layouts/_default/baseof.html b/website/themes/prosperon/layouts/_default/baseof.html
new file mode 100644
index 00000000..26144761
--- /dev/null
+++ b/website/themes/prosperon/layouts/_default/baseof.html
@@ -0,0 +1,13 @@
+
+
+
+ {{ partial "head.html" . }}
+
+
+ {{ partial "header.html" . }}
+
+ {{ block "main" . }}{{ end }}
+
+ {{ partial "footer.html" . }}
+
+
diff --git a/website/themes/prosperon/layouts/_default/list.html b/website/themes/prosperon/layouts/_default/list.html
new file mode 100644
index 00000000..1cb9bc15
--- /dev/null
+++ b/website/themes/prosperon/layouts/_default/list.html
@@ -0,0 +1,8 @@
+{{ define "main" }}
+
+
+
{{ .Title }}
+ {{ .Content }}
+
+
+{{ end }}
diff --git a/website/themes/prosperon/layouts/_default/single.html b/website/themes/prosperon/layouts/_default/single.html
new file mode 100644
index 00000000..1cb9bc15
--- /dev/null
+++ b/website/themes/prosperon/layouts/_default/single.html
@@ -0,0 +1,8 @@
+{{ define "main" }}
+
+
+
{{ .Title }}
+ {{ .Content }}
+
+
+{{ end }}
diff --git a/website/themes/prosperon/layouts/docs/list.html b/website/themes/prosperon/layouts/docs/list.html
new file mode 100644
index 00000000..2ddb0af4
--- /dev/null
+++ b/website/themes/prosperon/layouts/docs/list.html
@@ -0,0 +1,53 @@
+{{ define "main" }}
+
+
+
+
+ {{ if .Title }}
{{ .Title }}
{{ end }}
+ {{ .Content }}
+
+
+{{ end }}
diff --git a/website/themes/prosperon/layouts/docs/single.html b/website/themes/prosperon/layouts/docs/single.html
new file mode 100644
index 00000000..2ddb0af4
--- /dev/null
+++ b/website/themes/prosperon/layouts/docs/single.html
@@ -0,0 +1,53 @@
+{{ define "main" }}
+
+
+
+
+ {{ if .Title }}
{{ .Title }}
{{ end }}
+ {{ .Content }}
+
+
+{{ end }}
diff --git a/website/themes/prosperon/layouts/index.html b/website/themes/prosperon/layouts/index.html
new file mode 100644
index 00000000..1c754ca0
--- /dev/null
+++ b/website/themes/prosperon/layouts/index.html
@@ -0,0 +1,18 @@
+{{ define "main" }}
+
+
+
+ $25
+
+
No royalties
+
All updates through v1.x
+
Account on the Discourse
+
+
+
+
+
+ {{ .Content }}
+
+
+{{ end }}
diff --git a/website/themes/prosperon/layouts/partials/footer.html b/website/themes/prosperon/layouts/partials/footer.html
new file mode 100644
index 00000000..0a60b28b
--- /dev/null
+++ b/website/themes/prosperon/layouts/partials/footer.html
@@ -0,0 +1,7 @@
+
diff --git a/website/themes/prosperon/layouts/partials/head.html b/website/themes/prosperon/layouts/partials/head.html
new file mode 100644
index 00000000..0961cbaa
--- /dev/null
+++ b/website/themes/prosperon/layouts/partials/head.html
@@ -0,0 +1,14 @@
+
+
+{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} — {{ .Site.Title }}{{ end }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/themes/prosperon/layouts/partials/header.html b/website/themes/prosperon/layouts/partials/header.html
new file mode 100644
index 00000000..662cc0ca
--- /dev/null
+++ b/website/themes/prosperon/layouts/partials/header.html
@@ -0,0 +1,44 @@
+
+