Skip to main content
Minecraft Browser Edition demonstrates Longshot’s capability to coordinate complex 3D game development. Built with React Three Fiber and TypeScript, it showcases procedural terrain generation, voxel rendering, physics interactions, and persistent world state - all running entirely in the browser.

Project Overview

Timeline: 1-2 hours swarm run
Stack: React, Three.js, React Three Fiber, TypeScript, Vite
Runtime: Fully client-side, offline-capable after initial load
Source: examples/minecraft-browser/

Key Features

  • Procedural terrain generation with Simplex noise
  • Voxel rendering with chunk-based optimization
  • First-person controls - WASD movement + mouse look
  • Physics-based interactions using React Three Cannon
  • Block breaking and placing with real-time world updates
  • Day/night cycle with ambient lighting changes
  • Persistent world state stored in browser storage

Product Requirements

Product Statement

From SPEC.md:
A browser-based Minecraft clone built with React, React Three Fiber, and TypeScript. The player loads a single HTML page and is dropped into a procedurally generated 3D voxel world. They can walk around, break blocks, place blocks, manage inventory, and experience a day/night cycle. The project leverages the React ecosystem (Drei, Cannon) to provide a modern, declarative, and modular codebase.

Success Criteria (Ranked)

  1. Playable 3D voxel world renders at 30+ FPS with procedural terrain, leveraging React Three Fiber’s component architecture
  2. Player interactions - move (WASD + mouse), break blocks (left click), place blocks (right click) using physics
  3. Inventory and crafting system with React UI components for resource management
  4. Day/night cycle with ambient lighting making the world feel alive
  5. Zero-config build - compiles with zero TypeScript errors and runs from npm start

Hard Limits

  • Time budget: 1-2 hours wall clock (single swarm run)
  • Resource budget: Modern browser with WebGL 2.0 (no special GPU requirements)
  • External services: No paid APIs, no backend server, no database
  • Runtime mode: Fully client-side, works offline after initial load

Project Structure

src/
  components/           # React components
    Player.tsx          # First-person player controller
    Ground.tsx          # Terrain/chunk rendering
    Cube.tsx            # Individual voxel/block
    Inventory.tsx       # Inventory UI overlay
  hooks/                # Custom hooks
    useKeyboard.ts      # Keyboard input handling
    useStore.ts         # Zustand state management
  images/               # Texture assets
  App.tsx               # Canvas and Physics world setup
  main.tsx              # DOM entry point

Acceptance Tests

From SPEC.md, objective completion criteria:
# Build verification  
npm install && npm run build  # Exit code 0, no TypeScript errors
npm start                     # Serves game on localhost
Functional Tests:
  • Opening localhost in Chrome shows 3D voxel world
  • WASD keys move player through world
  • Mouse controls camera look direction
  • Left-clicking block removes it (updates state/mesh)
  • Right-clicking places selected block from inventory
  • Pressing E opens/closes inventory panel
  • Sky color transitions blue (day) → dark (night) over 5-minute cycle
  • Terrain is different each page reload (seeded random generation)
  • FPS counter shows 30+ FPS on standard laptop

Specification Documents

SPEC.md - Game Design Specification

Scope Model: Must Have (7):
  • Procedural terrain generation with Simplex noise
  • Chunk-based or optimized instance-mesh rendering
  • First-person camera with mouse look (PointerLockControls) + WASD movement
  • Physical interactions (jumping, collision) using Cannon
  • Block breaking/placing updating global store
  • Basic inventory UI (React DOM overlay) showing selected block
  • Day/night cycle (Sky component or custom)
Nice to Have (5):
  • Texture selector (1-9 keys)
  • Sound effects (use-sound or native Audio)
  • Advanced biomes
  • Ambient occlusion
  • Save/Load to LocalStorage persistence
Out of Scope:
  • Multiplayer / networking
  • Mobs / AI entities
  • Redstone logic
  • Infinite world generation (limited chunks)

AGENTS.md - Development Policies

Key Conventions:
## Code Style
- Strict TypeScript settings - no implicit any
- Follow existing naming and patterns
- Avoid placeholders and unfinished stubs  
- Keep functions small with explicit types

## Dependencies
Allowed: three, vite, typescript, vitest, playwright,
         @react-three/fiber, @react-three/drei, 
         @react-three/cannon, zustand, simplex-noise
         
Banned without approval: Large frameworks, full physics engines,
                               networking stacks

## Testing Policy
- Run targeted tests before completion
- Run all acceptance tests before handoff  
- Add/update tests for terrain determinism, block edits, persistence
- Never delete tests to hide failures

## Commit Expectations
- Focused commits mapping to acceptance tests
- Format: type(scope): concise summary
- Include rationale for non-obvious changes

Architecture Decisions

From DECISIONS.md:

Render Stack: Three.js + Custom Voxel Mesher

  • Date: 2026-02-14
  • Decision: Use Three.js for low-level rendering while owning chunk meshing and world simulation
  • Why: Three.js accelerates browser rendering setup while preserving control over voxel-specific performance and data structures
  • Alternatives: BabylonJS, PlayCanvas, raw WebGL2
  • Status: active

Persistence: Local-First Browser Storage

  • Date: 2026-02-14
  • Decision: Persist world edits in browser storage with explicit schema versioning
  • Why: Matches offline-first requirement and minimizes infrastructure dependencies
  • Alternatives: Backend-only persistence, IndexedDB wrapper service worker
  • Status: active

Testing Strategy: Unit + E2E Baseline

  • Date: 2026-02-14
  • Decision: Gate progress with deterministic unit tests for world logic and one browser E2E flow for place/reload persistence
  • Why: Protects core gameplay loop with fast feedback while validating real browser behavior
  • Alternatives: E2E only, unit only
  • Status: active

Setup and Execution

Prerequisites

Node.js 22 LTS
Modern browser (Chrome/Firefox/Safari) with WebGL 2.0

Quick Start

1

Install Dependencies

cd minecraft-browser
npm ci
2

Start Development Server

npm run dev
Vite will print the local URL (typically http://localhost:5173)
3

Play the Game

  1. Open the URL in your browser
  2. Click into the canvas to lock pointer controls
  3. Use WASD to move, mouse to look around
  4. Left click to break blocks, right click to place

Verification

npm run typecheck  # TypeScript compilation check
npm run test       # Unit tests
npm run build      # Production build  
npm run test:e2e   # End-to-end tests

Demo Flow

1

Launch Game

npm run dev
Open the game in a desktop browser
2

Test Controls

  • Move with WASD keys
  • Look around with mouse
  • Break block with left click
  • Place block with right click
3

Verify Persistence

  1. Break or place several blocks
  2. Note their positions
  3. Refresh the page
  4. Confirm your block edits persist in same coordinates

Implementation Highlights

Procedural Terrain Generation

The world uses Simplex noise for natural-looking terrain:
// Terrain generation with noise
import { createNoise2D } from 'simplex-noise'

const noise = createNoise2D()

function generateTerrain(chunkX: number, chunkZ: number) {
  const blocks = []
  
  for (let x = 0; x < CHUNK_SIZE; x++) {
    for (let z = 0; z < CHUNK_SIZE; z++) {
      const worldX = chunkX * CHUNK_SIZE + x
      const worldZ = chunkZ * CHUNK_SIZE + z
      
      // Generate height using noise
      const height = Math.floor(
        noise(worldX * 0.1, worldZ * 0.1) * 10 + 20
      )
      
      // Place blocks up to height  
      for (let y = 0; y < height; y++) {
        blocks.push({ x: worldX, y, z: worldZ, type: 'grass' })
      }
    }
  }
  
  return blocks
}

Physics-Based Player Controller

Using React Three Cannon for realistic movement:
import { useSphere } from '@react-three/cannon'
import { useFrame } from '@react-three/fiber'

function Player() {
  const [ref, api] = useSphere(() => ({
    mass: 1,
    type: 'Dynamic',
    position: [0, 20, 0],  
    ...defaultPlayerProps
  }))
  
  const { forward, backward, left, right, jump } = useKeyboard()
  
  useFrame(() => {
    const velocity = [0, 0, 0]
    
    if (forward) velocity[2] -= MOVE_SPEED
    if (backward) velocity[2] += MOVE_SPEED
    if (left) velocity[0] -= MOVE_SPEED  
    if (right) velocity[0] += MOVE_SPEED
    if (jump) velocity[1] = JUMP_FORCE
    
    api.velocity.set(...velocity)
  })
  
  return <mesh ref={ref} />
}

Block Interaction System

Raycast-based block breaking and placing:
import { useThree } from '@react-three/fiber'
import { Raycaster } from 'three'

function useBlockInteraction() {
  const { camera, scene } = useThree()
  const raycaster = new Raycaster()
  
  const breakBlock = () => {
    raycaster.setFromCamera({ x: 0, y: 0 }, camera)
    const intersects = raycaster.intersectObjects(scene.children)
    
    if (intersects.length > 0) {
      const hit = intersects[0]
      const blockPos = hit.point.sub(hit.face.normal.multiplyScalar(0.5))
      
      // Remove block from world state
      removeBlock(Math.floor(blockPos.x), 
                  Math.floor(blockPos.y), 
                  Math.floor(blockPos.z))
    }
  }
  
  const placeBlock = () => {
    raycaster.setFromCamera({ x: 0, y: 0 }, camera)
    const intersects = raycaster.intersectObjects(scene.children)
    
    if (intersects.length > 0) {
      const hit = intersects[0]  
      const blockPos = hit.point.add(hit.face.normal.multiplyScalar(0.5))
      
      // Add block to world state
      addBlock(Math.floor(blockPos.x),
               Math.floor(blockPos.y),
               Math.floor(blockPos.z),
               selectedBlockType)
    }
  }
  
  return { breakBlock, placeBlock }
}

State Management with Zustand

Global game state for blocks and inventory:
import create from 'zustand'

interface GameState {
  blocks: Map<string, Block>
  inventory: BlockType[]
  selectedBlock: number
  
  addBlock: (x: number, y: number, z: number, type: BlockType) => void  
  removeBlock: (x: number, y: number, z: number) => void
  setSelectedBlock: (index: number) => void
}

const useStore = create<GameState>((set) => ({
  blocks: new Map(),
  inventory: ['grass', 'dirt', 'stone', 'wood'],
  selectedBlock: 0,
  
  addBlock: (x, y, z, type) => set((state) => {
    const key = `${x},${y},${z}`
    state.blocks.set(key, { x, y, z, type })
    return { blocks: new Map(state.blocks) }
  }),
  
  removeBlock: (x, y, z) => set((state) => {
    const key = `${x},${y},${z}`  
    state.blocks.delete(key)
    return { blocks: new Map(state.blocks) }
  }),
  
  setSelectedBlock: (index) => set({ selectedBlock: index })
}))

Operational Runbook

Operating Modes

# Local development
npm run dev

# Swarm run  
codex --mode default --instruction-file AGENTS.md

# Recovery run
npm run dev -- --host 0.0.0.0 --strictPort

Monitoring

Key Logs:
  • Browser console for runtime errors
  • Terminal output from Vite for build issues
  • playwright-report/ for E2E test failures
Key Metrics:
  • Average frame time (target < 33ms for 30 FPS)
  • Chunk generation time
  • Chunk mesh rebuild counts
  • Save latency to browser storage
Failure Signals:
  • Persistent frame time above 33ms → reduce render distance
  • Chunk loads stalling near player → check mesh generation
  • Block edits not persisted after reload → check storage API
  • Repeated uncaught exceptions → check error boundaries

Recovery Procedures

Restart Orchestrator:
1

Stop Process

Ctrl+C to stop current dev process
2

Clear Artifacts

rm -rf node_modules/.vite
3

Restart and Validate

npm run dev
Load world and verify rendering
Partial Failure Handling:
  1. Identify failed component from logs: render loop, world generation, input, or persistence
  2. Isolate by disabling failing subsystem behind feature flag while keeping read-only world view
  3. Verify healthy state:
    npm run test
    
    Manual smoke test for movement and block editing
Resource Ceiling Behavior:
  • CPU cap: Reduce render distance and chunk meshing budget per frame
  • Memory cap: Evict far chunks from memory and compact block edit history
  • Disk cap: Rotate save snapshots - keep latest plus one rollback

Performance Optimization

Chunk-Based Rendering

Instead of rendering individual cubes, merge blocks into chunk meshes:
// Merge block geometry for entire chunk
function buildChunkMesh(blocks: Block[]): BufferGeometry {
  const geometries: BoxGeometry[] = []
  
  blocks.forEach(block => {
    const geo = new BoxGeometry(1, 1, 1)
    geo.translate(block.x, block.y, block.z)
    geometries.push(geo)  
  })
  
  return mergeBufferGeometries(geometries)
}

Frustum Culling

Only render chunks visible to camera:
function getVisibleChunks(cameraPos: Vector3, viewDistance: number) {
  return chunks.filter(chunk => {
    const distance = cameraPos.distanceTo(chunk.center)
    return distance < viewDistance * CHUNK_SIZE
  })
}

Level of Detail (LOD)

Simplify distant chunks:
function getChunkLOD(distance: number): number {
  if (distance < 50) return 0      // Full detail
  if (distance < 100) return 1     // Medium detail  
  return 2                          // Low detail
}

Key Learnings

What Worked Well

  1. React Three Fiber: Declarative 3D scene composition simplified complex rendering logic
  2. Zustand for state: Lightweight state management perfect for game state without Redux overhead
  3. Vite build tool: Lightning-fast hot reload during development
  4. Browser storage: Simple persistence without backend complexity

Challenges Addressed

  1. Performance at scale: Solved with chunk merging and frustum culling to maintain 30+ FPS
  2. Pointer lock issues: Handled with proper event listeners and escape key handling
  3. Physics stability: Tuned Cannon physics settings to prevent falling through world
  4. Memory leaks: Added proper cleanup in React useEffect for Three.js objects

Next Steps

Decagon Assistant

See full-stack AI application example

Basic Template

Start your own project from scratch

Project Structure

Learn the specification framework

Swarm Execution

Understand multi-agent coordination

Build docs developers (and LLMs) love