sg.js API Reference
Complete reference for the SiliconGhetto engine JavaScript API (sg.js).
import sg from 'https://cdn.siliconghetto.com/engine/v0.1/sg.js';
Lifecycle
sg.init(canvasId, width, height) — async
Initialize the engine. Loads the WASM module, selects hardware WebGPU by default, falls back to WebGL2 only when needed, and sets up the rendering context.
await sg.init('game-canvas', 800, 600);
sg.run({ update, draw })
Start the game loop. update() runs on the engine tick for game logic. draw() runs after for rendering.
sg.run({
update() { /* game logic */ },
draw() { /* rendering */ },
});
sg.stop()
Stop the game loop.
sg.clear(r, g, b)
Set the background clear color. Call at the start of draw().
sg.clear(0.1, 0.1, 0.2); // dark blue
Time
| Property | Type | Description |
|---|
sg.dt | number | Delta time in seconds since last frame |
sg.elapsed | number | Total elapsed time in seconds |
sg.frame | number | Total frame count |
Keyboard
| Method | Returns | Description |
|---|
sg.keyDown(key) | boolean | Key is currently held |
sg.keyPressed(key) | boolean | Key was pressed this frame |
Key names use KeyboardEvent.code values: 'KeyW', 'ArrowUp', 'Space', 'ShiftLeft', etc.
if (sg.keyDown('KeyW')) { /* moving forward */ }
if (sg.keyPressed('Space')) { /* just jumped */ }
Mouse
| Property/Method | Type | Description |
|---|
sg.mouseX | number | Mouse X position on canvas |
sg.mouseY | number | Mouse Y position on canvas |
sg.mouseDX | number | Mouse X movement delta (works under pointer lock) |
sg.mouseDY | number | Mouse Y movement delta |
sg.scrollDelta | number | Scroll wheel delta (positive = down) |
sg.mouseButton(btn) | boolean | Mouse button held (0=left, 1=middle, 2=right) |
sg.lockPointer() | void | Request pointer lock for FPS-style mouse look |
sg.pointerLocked | boolean | Whether pointer is currently locked |
Entities (2D)
sg.spawn(opts) → number
Create an entity. Returns a numeric handle used to reference the entity in all other calls.
const player = sg.spawn({
x: 400, y: 300,
sprite: { w: 32, h: 32, r: 1, g: 0.3, b: 0.3 },
collision: { w: 32, h: 32 },
tag: 'player',
});
| Option | Type | Description |
|---|
x, y | number | Initial position |
sprite | { w, h, r?, g?, b?, a? } | Size and color |
collision | { w, h } | Collision box dimensions |
tag | string | Tag for finding entities later |
texture | string | Texture ID (loaded with loadTexture) |
sg.despawn(entity)
Remove an entity and clean up all subsystem state (health, physics, AI, animation).
Position & Velocity
| Method | Description |
|---|
sg.setPos(e, x, y) | Set 2D position |
sg.setPos3D(e, x, y, z) | Set 3D position |
sg.getX(e) | Get X position |
sg.getY(e) | Get Y position |
sg.setVelocity(e, vx, vy) | Set velocity |
sg.getVelocity(e) | Get velocity → { vx, vy } |
Appearance
| Method | Description |
|---|
sg.setSprite(e, w, h, r, g, b, a) | Set size and color |
sg.setTexture(e, textureId) | Set texture |
| Method | Description |
|---|
sg.tag(e, tag) | Set a tag |
sg.hasTag(e, tag) | Check for tag |
sg.findByTag(tag) | Find first entity with tag |
Camera
| Method | Description |
|---|
sg.setCamera(x, y, zoom) | Set 2D camera position and zoom |
sg.setCamera3D(px, py, pz, tx, ty, tz) | Set 3D camera (position → target) |
sg.cameraFollow(entity, lerpSpeed) | Camera smoothly follows an entity |
Drawing
Call these inside draw(). All coordinates are screen-space pixels.
| Method | Description |
|---|
sg.drawAll() | Draw all entities |
sg.drawRect(x, y, w, h, { r, g, b, a }) | Draw a filled rectangle |
sg.drawCircle(x, y, radius, { r, g, b, a }) | Draw a filled circle |
sg.drawLine(x1, y1, x2, y2, { thickness, r, g, b, a }) | Draw a line |
sg.drawText(text, x, y, size, { r, g, b }) | Draw text |
sg.drawSprite(textureId, x, y, w, h) | Draw a textured sprite |
sg.drawRect(10, 10, 200, 30, { r: 0, g: 0, b: 0, a: 0.5 });
sg.drawText('Score: 100', 15, 15, 14, { r: 1, g: 1, b: 1 });
Screen
| Property | Type | Description |
|---|
sg.screenWidth | number | Canvas width in pixels |
sg.screenHeight | number | Canvas height in pixels |
| Method | Description |
|---|
sg.resize(width, height) | Resize canvas and GPU surfaces |
Physics (2D)
Collision Detection
| Method | Returns | Description |
|---|
sg.checkCollision(a, b) | boolean | AABB overlap test between two entities |
sg.overlapping(e) | number[] | All entities overlapping with e |
sg.moveAndCollide(e, dx, dy) | boolean | Move entity, stop at collisions |
Full platformer controller with gravity, jumping (coyote time + jump buffer), and variable jump height.
const player = sg.spawn({ x: 100, y: 100, collision: { w: 16, h: 24 } });
sg.setPlatformerPhysics(player, {
gravity: 980, jumpForce: 400, maxFall: 600, speed: 200,
coyoteTime: 0.1, jumpBufferTime: 0.1,
});
// In update():
const input = sg.keyDown('ArrowRight') ? 1 : sg.keyDown('ArrowLeft') ? -1 : 0;
if (sg.keyPressed('Space')) sg.platformerJump(player);
sg.platformerJumpHeld(player, sg.keyDown('Space'));
const { dx, dy } = sg.updatePlatformer(player, input);
const { hitX, hitY } = sg.tilemapCollide(sg.getX(player), sg.getY(player), 8, 12, dx, dy);
sg.platformerResolve(player, hitX, hitY, dy > 0);
| Method | Description |
|---|
sg.setPlatformerPhysics(e, opts) | Enable platformer physics (gravity, jumpForce, maxFall, speed, coyoteTime, jumpBufferTime) |
sg.updatePlatformer(e, inputX) | Compute movement delta → { dx, dy } |
sg.platformerJump(e) | Request jump (buffered) |
sg.platformerJumpHeld(e, held) | Set jump button hold state |
sg.platformerResolve(e, hitX, hitY, wasMovingDown) | Resolve collision |
sg.onGround(e) | boolean — is entity on ground? |
sg.facing(e) | -1 or 1 — facing direction |
sg.onWallLeft(e) | boolean — touching wall on left |
sg.onWallRight(e) | boolean — touching wall on right |
Top-Down Physics
sg.setTopDownPhysics(entity, { speed: 150, friction: 600 });
Tile Maps
Grid-based level system with collision.
sg.createTileMap(20, 15, 32); // 20x15 grid, 32px tiles
sg.setTile(5, 14, 1); // solid ground
sg.setTileFlags(3, 10, 2, 2); // platform (one-way)
// In draw():
sg.drawTileMap();
| Method | Description |
|---|
sg.createTileMap(w, h, tileSize) | Create grid |
sg.setTile(x, y, type) | Set tile (0=empty) |
sg.setTileFlags(x, y, type, flags) | Set tile with collision flags (1=solid, 2=platform, 4=ladder, 8=hazard) |
sg.getTile(x, y) | Get tile type |
sg.getTileFlags(x, y) | Get collision flags |
sg.drawTileMap() | Render the map |
sg.tilemapCollide(cx, cy, halfW, halfH, dx, dy) | AABB collision → { x, y, hitX, hitY } |
sg.tileAtWorld(wx, wy) | Get tile at world position |
sg.isTileHazard(x, y) | Check hazard flag |
sg.isTileLadder(x, y) | Check ladder flag |
sg.clearTileMap() | Destroy tile map |
Animation
Frame-based sprite animation system.
sg.addAnimation(player, 'run', { frames: [0, 1, 2, 3], fps: 10, loop: true });
sg.addAnimation(player, 'idle', { frames: [0], fps: 1 });
sg.playAnim(player, 'run');
// In draw():
const frame = sg.getAnimFrame(player); // current frame index
| Method | Description |
|---|
sg.addAnimation(e, name, { frames, fps, loop }) | Define animation clip |
sg.playAnim(e, name) | Play animation |
sg.getAnimFrame(e) | Current frame index (-1 if none) |
sg.animationName(e) | Currently playing animation name |
sg.animationFinished(e) | Has non-looping animation finished? |
Health & Damage
sg.setHealth(enemy, 50); // 50 HP
sg.damage(enemy, 20); // -20 HP, triggers invincibility
sg.heal(enemy, 10); // +10 HP (capped at max)
if (sg.isDead(enemy)) sg.despawn(enemy);
| Method | Returns | Description |
|---|
sg.setHealth(e, max) | | Set max HP (also sets current to max) |
sg.damage(e, amount) | boolean | Deal damage (false if invincible) |
sg.heal(e, amount) | | Restore HP |
sg.getHealth(e) | number | Current HP (-1 if no health component) |
sg.getMaxHealth(e) | number | Max HP |
sg.isDead(e) | boolean | HP <= 0 |
sg.healthFraction(e) | number | HP / maxHP (0.0 to 1.0) |
sg.healthVisible(e) | boolean | Visible during hit flicker |
sg.updateHealth() | | Update invincibility timers (call in update()) |
sg.revive(e) | | Reset HP to max |
sg.setInvincibilityDuration(e, secs) | | Set i-frame duration |
Projectiles
const bullet = sg.spawnProjectile(x, y, 1, 0, 300, {
damage: 25, lifetime: 2, owner: player,
w: 8, h: 4, r: 1, g: 0.8, b: 0,
});
// In update():
sg.updateProjectiles();
for (const { handle, damage } of sg.checkProjectileHits()) {
sg.damage(handle, damage);
}
// In draw():
sg.drawProjectiles();
| Method | Description |
|---|
sg.spawnProjectile(x, y, dirX, dirY, speed, opts) | Spawn projectile → ID |
sg.updateProjectiles() | Move all projectiles (call in update()) |
sg.drawProjectiles() | Render all projectiles (call in draw()) |
sg.checkProjectileHits() | Check hits → [{ handle, damage }] |
sg.projectileCount | Active projectile count |
sg.clearProjectiles() | Remove all projectiles |
AI Behaviors
sg.setAI(enemy, 'chase', { target: player, speed: 80 });
sg.setAI(guard, 'patrol', { points: [[100,200], [300,200]], speed: 60 });
sg.setAI(rabbit, 'flee', { target: player, speed: 120, maxDistance: 200 });
// In update():
for (const { handle, dx, dy, facing } of sg.updateAI()) {
const { hitX, hitY } = sg.tilemapCollide(sg.getX(handle), sg.getY(handle), 8, 12, dx, dy);
sg.setPos(handle, sg.getX(handle) + (hitX ? 0 : dx), sg.getY(handle) + (hitY ? 0 : dy));
}
| Behavior | Options | Description |
|---|
'chase' | { target, speed } | Move toward target entity |
'patrol' | { points, speed } | Walk between waypoints |
'flee' | { target, speed, maxDistance } | Run away from target |
2D Spatial Index
Fast broadphase queries for busy 2D games with many actors, pickups, hitboxes, or sensors.
const spatial = sg.createSpatialIndex({ cellSize: 64 });
spatial.insert({ entity: player, x: sg.getX(player), y: sg.getY(player), w: 18, h: 28 });
for (const enemy of enemies) {
spatial.insert({ entity: enemy, x: sg.getX(enemy), y: sg.getY(enemy), w: 24, h: 24 });
}
const nearby = spatial.queryRadius(sg.getX(player), sg.getY(player), 160);
const hit = spatial.raycast(sg.getX(player), sg.getY(player), sg.mouseX, sg.mouseY, [player]);
for (const { a, b } of spatial.pairs()) {
// Run exact gameplay collision only for nearby overlapping candidates.
}
| Method | Description |
|---|
sg.createSpatialIndex({ cellSize }) | Create a reusable 2D broadphase grid |
spatial.insert({ entity, x, y, w, h }) | Insert or replace an object’s bounds |
spatial.remove(entity) | Remove an object by ID |
spatial.queryRect(x, y, w, h) | Return items intersecting a rectangle |
spatial.queryRadius(x, y, radius) | Return entities within a radius, sorted nearest first |
spatial.nearest(x, y, radius) | Return nearest entity ID, or 0 |
spatial.pairs() | Return unique overlapping broadphase pairs |
spatial.get(entity) | Return the indexed item, or undefined |
spatial.raycast(x0, y0, x1, y1, ignore) | Return the first bounds hit along a line segment |
spatial.sweepAabb(x, y, w, h, dx, dy, ignore) | Sweep a moving box and return the first hit |
spatial.clear() | Remove all indexed items |
Kinematic Controller
const solids = sg.createSpatialIndex({ cellSize: 64 });
const playerBody = sg.createKinematicController2D({
entity: player,
spatial: solids,
x: sg.getX(player),
y: sg.getY(player),
w: 18,
h: 28,
});
const move = playerBody.move(inputX * speed * sg.dt, velocityY * sg.dt);
sg.setPos(player, move.x, move.y);
if (move.grounded) velocityY = 0;
| Method | Description |
|---|
sg.createKinematicController2D(opts) | Create a swept 2D controller backed by a spatial index |
body.move(dx, dy, ignore) | Move with swept collision, sliding, and contact flags |
body.snapDown(distance, ignore) | Stick to nearby ground after movement |
body.teleport(x, y) | Set position immediately |
body.sync() | Reinsert the current body bounds into the spatial index |
sg.createGame(opts) — async
Primary entry point for new games. It composes runtime options, world setup, content packs, and capability declarations.
const game = await sg.createGame({
runtime: {
tickRate: 60,
deterministic: true,
saveProfile: 'sg.save.v2',
},
world: {
canvas: 'game-canvas',
topology: 'chunked-grid-3d',
streaming: 'player-centric',
seed: 42,
},
packs: ['sg.survival@0.1.0'],
capabilities: ['inventory', 'crafting', 'procedural_world'],
});
sg.registeredPacks()
Returns first-party pack metadata for composition tooling:
const [{ id, version, capabilities }] = sg.registeredPacks();
| Option | Default | Description |
|---|
runtime.tickRate | 60 | Deterministic simulation tick rate |
runtime.deterministic | true | Use deterministic runtime assumptions |
runtime.saveProfile | 'sg.save.v2' | Save/migration profile identifier |
world.topology | 'chunked-grid-3d' | World topology |
world.streaming | 'player-centric' | Chunk streaming strategy |
packs | [] | Registered content packs, either strings like 'sg.survival@0.1.0' or { id, version } objects. Use sg.registeredPacks() to inspect first-party IDs and versions. Omitting the version uses the registered first-party version. Unknown pack IDs or unsupported versions throw during composition. |
capabilities | [] | Requested platform capabilities |
Returns { seed, canvas, width, height, runtime, world, packs, capabilities, composition, template, game }.
composition.bootMode is 'base-runtime' for a base world and 'pack-template' when a first-party pack boots through a pack template.
Block Worlds
Quick Start
const { seed } = await sg.createGame({
world: {
canvas: 'game-canvas',
worldHeight: 64,
seed: 42,
},
});
sg.run({
update() { sg.fpsUpdate(); },
draw() { sg.clear(0.5, 0.7, 1.0); },
});
sg.createGame(opts) — async
Platform setup: initializes engine, creates a voxel world, generates biome-driven terrain, enables FPS controller, and sets up pointer lock.
| Option | Default | Description |
|---|
canvas | 'game-canvas' | Canvas element ID |
width | 960 | Canvas width |
height | 600 | Canvas height |
world.worldHeight | 64 | Y height |
seed | random | Terrain generation seed |
player | {} | FPS controller settings (see below) |
biomes | defaults | Custom biome array (replaces defaults) |
spawn | center | [x, y, z] spawn position |
Returns { seed, canvas, width, height }.
Block Operations
| Method | Description |
|---|
sg.createVoxelWorld(sx, sy, sz) | Create empty voxel world |
sg.setBlock(x, y, z, type) | Set a block (auto-remeshes chunk) |
sg.getBlock(x, y, z) | Get block type at position |
sg.fillBlocks(x1, y1, z1, x2, y2, z2, type) | Fill region with block type |
sg.voxelSizeX/Y/Z | World dimensions |
Block Types
Access via sg.BLOCKS or sg.blockTypes:
sg.setBlock(x, y, z, sg.BLOCKS.STONE);
sg.setBlock(x, y, z, sg.BLOCKS.DIAMOND_ORE);
| ID | Constant | ID | Constant |
|---|
| 0 | AIR | 22 | CALCITE |
| 1 | DIRT | 23 | FLOWER_YELLOW |
| 2 | GRASS | 24 | FLOWER_RED |
| 3 | STONE | 25 | TALL_GRASS |
| 4 | SAND | 26 | DEAD_BUSH |
| 5 | WOOD | 27 | CACTUS |
| 6 | LEAVES | 28 | MUSHROOM_RED |
| 7 | WATER | 29 | MUSHROOM_BROWN |
| 8 | BRICK | 30 | CHERRY_LEAVES |
| 9 | COBBLESTONE | 31 | BIRCH_WOOD |
| 10 | PLANKS | 32 | SPRUCE_WOOD |
| 11 | SNOW | 33 | JUNGLE_WOOD |
| 12 | GRAVEL | 34 | ACACIA_WOOD |
| 13 | PACKED_ICE | 35 | GLOW_LICHEN |
| 14 | SANDSTONE | 36 | IRON_ORE |
| 15 | RED_SAND | 37 | GOLD_ORE |
| 16 | TERRACOTTA | 38 | DIAMOND_ORE |
| 17 | PODZOL | 39 | COAL_ORE |
| 18 | MYCELIUM | 40 | LAVA |
| 19 | MUD | 41 | DRY_GRASS |
| 20 | CLAY | 42 | SPRUCE_LEAVES |
| 21 | MOSSY_STONE | | |
Block names are available via sg.BLOCK_NAMES[id].
FPS Controller
Enabled automatically by createGame(), or manually:
sg.enableFPSController({
speed: 7, // walk speed
jumpForce: 8.5, // jump velocity
gravity: 22, // gravity strength
sensitivity: 0.003, // mouse look sensitivity
flySpeed: 14, // fly mode speed
sprintMultiplier: 1.6,// shift sprint
playerRadius: 0.28, // collision cylinder radius
playerHeight: 1.65, // collision cylinder height
});
Built-in controls: WASD (move), Mouse (look), Space (jump), Shift (sprint), F (fly toggle), Ctrl (fly down).
| Method | Returns | Description |
|---|
sg.fpsUpdate() | { x, y, z, eyeY, targetX/Y/Z, onGround, flyMode, sprinting } | Update controller (call in update()) |
sg.fpsSetPosition(x, y, z) | | Teleport player |
sg.fpsGetPosition() | { x, y, z } | Player feet position |
sg.fpsGetLook() | { yaw, pitch } | Camera angles (radians) |
sg.fpsSetFly(bool) | | Set fly mode |
Raycasting
DDA (Digital Differential Analyzer) — exact, never misses a block.
// From FPS eye position:
const hit = sg.voxelRaycast(7);
if (hit.hit) {
// hit.x, hit.y, hit.z — the block that was hit
// hit.prevX, hit.prevY, hit.prevZ — the air block before it (for placing)
// hit.blockType — block type ID
}
// From arbitrary position + direction:
const hit2 = sg.voxelRaycastFrom(ox, oy, oz, dx, dy, dz, 20);
Collision
| Method | Returns | Description |
|---|
sg.voxelCollide(px, py, pz, dx, dy, dz, radius, height) | { x, y, z, hitY } | AABB collision (standalone) |
sg.voxelFindSpawnY(x, z) | number | Highest solid block + 2 at column |
sg.voxelIsWater(x, y, z) | boolean | Is block water? |
Terrain Generation
sg.generateTerrain({ seed: 42 });
Generates terrain using simplex noise with biome-driven terrain, cave systems, Y-level ore distribution, biome-specific trees, and surface decoration.
Biome System
Default biomes are registered automatically. To customize:
// Replace all biomes with custom set:
sg.resetBiomes();
sg.addBiome({
name: 'Alien Desert',
tempRange: [0.5, 1.0],
moistRange: [-1, 0],
surface: sg.BLOCKS.RED_SAND,
subSurface: sg.BLOCKS.TERRACOTTA,
treeDensity: 0.02,
treeTrunk: sg.BLOCKS.CACTUS,
treeLeaves: sg.BLOCKS.AIR,
});
sg.generateTerrain({ seed: 42 });
| Method | Description |
|---|
sg.addBiome(opts) | Add a biome (first match wins) |
sg.resetBiomes() | Clear all biomes (for custom set) |
sg.clearBiomes() | Reset to 20 defaults |
sg.biomeCount | Number of registered biomes |
Assets
await sg.loadTexture('hero', 'hero.png');
await sg.loadSound('jump', 'jump.wav');
sg.drawSprite('hero', 100, 200, 32, 32);
sg.playSound('jump');
| Method | Description |
|---|
sg.loadTexture(id, url) | Load texture (async) |
sg.loadSound(id, url) | Load audio (async) |
sg.playSound(id) | Play sound effect |
sg.playMusic(id) | Play looping music |
sg.stopMusic() | Stop music |
sg.setVolume(v) | Set master volume (0.0–1.0) |
CDN Library
await sg.loadTexture('hero', sg.lib.texture('hero-spritesheet'));
await sg.loadSound('jump', sg.lib.sound('jump'));
Particles
const emitter = sg.createEmitter(400, 300);
sg.emitBurst(emitter, 50); // spawn 50 particles
3D Meshes
const cube = sg.createCube();
const sphere = sg.createSphere();
const plane = sg.createPlane();