Skip to content

OpenStaticFish/voxl

Repository files navigation

VOXL — Voxel Sandbox Prototype

A browser-based WebGL voxel sandbox prototype with procedural terrain, chunk streaming, block editing, creative flight, and screenshot readiness. Built as a compact but convincing creative-mode slice with its own original visual identity (no copyrighted assets).

VOXL is an original indie prototype. It is not Minecraft, is not affiliated with Mojang/Microsoft, and uses no Minecraft assets, branding, or code.


1. Product interpretation

A focused, playable voxel sandbox you can drop into in seconds: a main menu, a generated world with biomes (plains, forest, desert, mountains), water, beaches, trees and caves, gravity + jumping, creative flight, block breaking/placement, a hotbar, pause/settings, and clouds. The priority is a stable, playable, good-looking core rather than a huge superficial feature list. The world is deterministic from a seed and designed to compose well in screenshots.


2. Tooling choice & justification

Stack: Three.js + Vite + TypeScript, run with Bun.

Choice Why
Three.js The right weight for a voxel game: direct BufferGeometry/index control for chunk meshing + face culling, a solid WebGL2 renderer, and lighting/sky primitives. Far lighter than a full engine like Babylon, far more capable than raw WebGL (which would force re-implementing matrices/meshing/shaders).
Vite Instant dev server with HMR, optimized production build, and vite preview — zero config for a TS+Three app.
TypeScript Voxel/world/meshing code has lots of data structures that benefit from typing; strict mode catches real bugs.
Bun Required runtime/package manager; also runs the optional screenshot script.

Why not something else?

  • Raw WebGL — too much boilerplate (custom shader/matrix/meshing pipelines); would slow delivery and reduce quality. Too limited.
  • Babylon.js — heavier full-game-engine abstraction (scene graph, inspectors, GUI) we don't need for a hand-rolled chunk mesher. Overkill.
  • React / React-Three-Fiber — DOM/component abstraction adds overhead to a tight per-frame game loop and buys little for a fullscreen canvas game with custom UI overlays. Overkill here.
  • A big engine (PlayCanvas, Phaser) — voxel meshing is custom anyway; an engine's 2D/scene conveniences don't help. Overkill.

The result is not overkill (Three is the minimum that gives us real meshing + lighting) and not too limited (we get performant chunk meshes, sky shaders, and clean TS). Dependencies are intentionally tiny: only three, plus vite/typescript/@types/three in dev. Playwright is an optional dev dependency for automated screenshots.


3. Implementation overview

index.html                  DOM for all screens + HUD (single source of UI structure)
src/
  main.ts                   Bootstraps the Game; exposes window.__voxl automation hook
  constants.ts              Tunables (chunk size, physics, reach, budgets)
  types.ts                  Shared types (Settings, FaceDef, RaycastHit, GameState)
  engine/
    Renderer.ts             WebGLRenderer wrapper (canvas, DPI, tone mapping, preserveDrawingBuffer)
    Noise.ts                Seeded Perlin noise + fBm (fully deterministic from seed)
    Textures.ts             Procedural 16px texture atlas drawn to canvas (no external assets)
    Sky.ts                  Gradient sky dome shader + sun + ambient/hemi lights + scrolling clouds
    Input.ts                Keyboard/mouse state, mouse-look deltas, pointer lock, double-tap-space
    Screenshot.ts           Canvas → PNG (File System Access API, else download)
  game/
    Blocks.ts               Block registry (tiles per face, solid/opaque/liquid flags) + hotbar
    Chunk.ts                16×16×64 Uint8Array block store + dirty/version flags
    ChunkMesher.ts          Face-culled chunk geometry → opaque + transparent buffers (baked shading)
    TerrainGenerator.ts     Heightmap + biomes + caves + trees + water, deterministic from seed
    World.ts                Chunk map, streaming (gen+mesh budgets), get/setBlock, mesh lifecycle
    Player.ts               Camera, mouse-look, walk/fly, gravity, swept-AABB voxel collision, raycast
    BlockRaycaster.ts       Amanatides–Woo voxel DDA for precise block picking
    Game.ts                 Orchestrator: loop, state machine, editing, hotbar, settings, screenshots
  ui/
    ui.css                  Cohesive indie-voxel visual identity
    ScreenManager.ts        DOM screen visibility + transitions
    HUD.ts                  Crosshair, hotbar, fps/coords/mode badges, toast
    Menus.ts                Main menu / pause / controls / settings wiring
  state/
    Settings.ts             Default settings + localStorage persistence
scripts/
    screenshot.ts           Optional Playwright capture for main-menu.png + in-game.png

Key engineering points

  • Chunk meshing builds one indexed BufferGeometry per chunk (opaque + a separate transparent pass for water). Only visible faces are emitted; faces are culled against opaque neighbors, and same-type transparent neighbors (water–water). Directional brightness is baked into vertex colors so the world reads clearly.
  • Streaming generates and meshes a budgeted number of chunks per frame (closest-first spiral) and unloads far chunks, disposing their geometry. Block edits mark the chunk (and border neighbors) dirty and rebuild meshes immediately.
  • Collision is per-axis swept AABB against voxel solidity; fly/walk/swim states are all handled. Bedrock is unbreakable; placement inside the player is blocked.
  • Determinism: terrain derives entirely from the seed string via seeded Perlin.

4. Setup & run (Bun)

Requirements: Bun and a modern desktop browser (Chrome/Firefox/Edge/Safari).

bun install          # install dependencies
bun run dev          # start BOTH dev servers (game + website)

bun run dev runs a small orchestrator (scripts/dev.ts) that launches two processes with prefixed logs and one Ctrl-C to stop both:

Tag Server URL
[game] Vite HMR for the voxel game itself http://localhost:5173
[site] Astro dev for the marketing site (embeds the game at /play) http://localhost:4321

Open the site URL and click Play to run the game in-browser, or open the game URL directly for the bare game with HMR. Click the canvas if pointer lock didn't engage. Press Ctrl-C once to stop both servers.

The website lives in website/ (see website/README.md). Granular commands: bun run game:dev (game only), bun run site:dev (site only), bun run site:build, bun run site:preview.

Build / preview / checks

bun run build        # production build → dist/
bun run preview      # serve the production build (http://localhost:4173)
bun run typecheck    # tsc --noEmit  (type checking)
bun run check        # alias for typecheck

All four are verified working in this environment (see §10).


5. Controls

Action Input
Move W A S D
Look Mouse (pointer lock)
Jump / Swim up / Fly up Space
Fly down / descend Shift
Toggle flight Space ×2 (double-tap)
Sprint Ctrl
Break block Left mouse
Place block Right mouse
Select hotbar slot 18 / scroll wheel / F to cycle
Capture screenshot P
Pause / release mouse Esc

Flight, gravity, and swimming are all reflected in the on-screen mode badge (Walking / Flying / Swimming). Controls are also listed under Controls in the main menu.


6. Settings

Available from the main menu and the pause screen; changes apply live:

  • View distance (chunk radius 2–10) — affects generation, meshing, and fog.
  • Mouse sensitivity
  • Field of view (60–110)
  • Show FPS
  • Clouds on/off
  • World seed + Regenerate (rebuilds the world from a new seed)

Settings persist to localStorage.


7. Screenshots

There are two ways to capture the required screenshots (screenshots/main-menu.png and screenshots/in-game.png). Both are real captures from the running game — no fakes.

A. Automated (recommended) — scripts/screenshot.ts

Uses Playwright to capture full-viewport screenshots (so DOM overlays like the menu and HUD are included alongside the canvas). Run a server first, then the script:

bun add -D playwright
bunx playwright install chromium
bun run dev                       # or: bun run build && bun run preview
VOXL_URL=http://localhost:5173 bun run screenshot

(If you used preview, point VOXL_URL at http://localhost:4173.)

The script waits for the menu, captures screenshots/main-menu.png, starts a game, waits for chunks to stream in, then captures screenshots/in-game.png.

If Playwright fails to launch under Bun on your machine, run the same script with Node instead: VOXL_URL=http://localhost:5173 node scripts/screenshot.ts.

B. Manual (always works, no extra deps)

  1. bun run dev, open the browser.
  2. Main menu: on the title screen, press P. The canvas PNG downloads — save it as screenshots/main-menu.png.
    • Note: the P shortcut captures the WebGL canvas. The menu is a DOM overlay, so for a menu screenshot that includes the title/buttons use your OS screenshot (or method A). The canvas behind the menu is the sky, so the P capture is mainly useful in-game.
  3. In-game: click Play, look at a nice view (sky + terrain + trees + water), press P. Save the download as screenshots/in-game.png.

Browser security prevents web pages from writing directly to a chosen folder, so captures are delivered as a download (or via the File System Access API "Save" dialog where supported). Save them with the exact filenames above.

Environment note (honest): automated capture could not be executed in the build environment because the headless server is missing Chromium's system libraries (libglib-2.0.so.0, etc.) and there is no package manager/sudo to install them. The script is implemented and reaches the browser-launch stage correctly; run it on a normal desktop machine. The in-game P shortcut and OS screenshot also work fully locally.


8. Performance notes

  • One indexed mesh per chunk (opaque) + one for water; no per-block meshes. Face culling keeps triangle counts low.
  • Budgeted generation (2 chunks/frame) and meshing (2 chunks/frame) prevent frame hitches while streaming; spawn area is pre-generated + pre-meshed so the first frame already looks good.
  • Far chunks are unloaded and their geometries disposed (no GPU/memory leak). Shared materials are never disposed per chunk.
  • pixelRatio capped at 2; fog hides the view-distance edge and blends to sky.
  • Production bundle ≈ 132 kB gzipped (mostly Three.js).

Typical playable range: view distance 4–8 on a normal laptop is smooth.


9. Verification checklist

Check Status
bun install works
bun run dev works
bun run build works
bun run preview works
bun run typecheck / bun run check works
Framework/tooling choice justified (Three.js + Vite + TS + Bun)
Bun is the package manager and script runner
Main menu
In-game HUD (crosshair, hotbar, fps/coords/mode)
Procedural voxel terrain
Chunk-based loading / chunk organization
Block data supports edits (setBlock updates world data + remeshes)
Efficient visible-face / chunk-mesh rendering
Gravity-based movement + jumping
Creative flight (double-tap Space)
Mouse-look controls (pointer lock)
Block breaking (left click; bedrock protected)
Block placement (right click; blocked inside player)
Hotbar block selection (1–8 / scroll / F)
Pause / menu / settings UI
View distance control (live)
Mouse sensitivity control (live)
Clouds / sky treatment
Terrain variation (biomes, hills, mountains, water, beaches, trees, caves)
Controls documented in UI + README
Screenshot workflow documented
screenshots/main-menu.png can be captured from the actual game ✅*
screenshots/in-game.png can be captured from the actual game ✅*
No fake / placeholder screenshots claimed as real
No fake compliance or placeholder-only gameplay systems

* Capturable via the implemented P shortcut / Playwright script / OS screenshot. Could not be executed in the build sandbox due to missing Chromium system libraries (no GUI stack); run locally.


10. Commands actually run in this environment

bun install            → ok (20 packages)
bun run typecheck      → ok (tsc --noEmit, 0 errors)
bun run build          → ok (dist/ built, ~132 kB gzip)
bun run preview        → ok (HTTP 200 on http://localhost:4173/)
node scripts/screenshot.ts → reached Chromium launch; failed ONLY because the
                          headless host lacks libglib/etc. (no apt, no sudo)

11. Limitations & suggested next improvements

  • No per-vertex ambient occlusion. Shading is baked directional face tint + Lambert lights; AO would add depth to screenshots. (Meshing is structured to add it later.)
  • Canvas-only P capture excludes DOM overlays (HUD/menu). Full-viewport capture needs the Playwright script or an OS screenshot. (Could render an orthographic HUD pass into the canvas to make P complete.)
  • Caves are simple 3D-noise pockets/tunnels rather than full cave systems.
  • No day/night cycle, mobs, inventory crafting, or sounds — out of scope for this slice.
  • Water is a flat transparent surface with light swim physics; no waves/flow.
  • Automation not executable in the build sandbox (missing GUI libs).
  • Mobile: UI doesn't break, but touch controls are not implemented (documented as optional).
  • Trees are kept within-chunk to avoid cross-chunk writes; chunk borders are slightly sparser as a result.

12. Honest self-critique

  • What's strong: clean separation (engine/game/ui/state); deterministic world; budgeted streaming with proper disposal; swept-AABB collision with fly/walk/swim; precise voxel DDA picking; cohesive original UI; live settings; a real (if environment-blocked) automated screenshot path plus a guaranteed manual path.
  • What's weakest: no AO (biggest visual-quality miss for screenshots); the P-key canvas capture can't include the DOM HUD; caves are basic; and I could not produce the actual PNGs in-sandbox because the host has no GUI stack — so the screenshot deliverables are implemented and documented but must be generated on a normal machine.
  • Trade-off I'd revisit: I disabled noUncheckedIndexedAccess because voxel array indexing is pervasive and construction-bounds-safe; this trades a bit of type rigor for readable code. With more time I'd add focused bounds helpers and re-enable it.

About

VOXL — an original browser-based WebGL voxel sandbox prototype (Minetest/Luanti-style worldgen) with its Astro marketing site.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors