GCanvas Coordinate System

Understanding how positioning works throughout the library

Overview

GCanvas uses a center-based coordinate system where x and y refer to an object's center point, not its top-left corner. This makes rotation and scaling more intuitive since transforms happen around the object's center.

TL;DR - Quick Reference

Situation How It Works
Object at (100, 100) Center is at screen position (100, 100)
Child at (10, 20) in Scene at (100, 100) Child center is at screen (110, 120)
Camera3D project() returns (0, 0) Object is at the center of the 3D view
Scene3D at (width/2, height/2) 3D origin appears at canvas center

1. Center-Based Positioning

Unlike many graphics libraries that use top-left corners, GCanvas positions objects by their center point. When you set x: 100, y: 100, the object's center is at that position.

Why Center-Based?

Interactive Demo: Center vs Bounds
// This circle's CENTER is at (100, 100)
const circle = new Circle(50, { x: 100, y: 100, color: "#0f0" });

// The circle extends from:
// - Left edge: 50 (100 - radius)
// - Right edge: 150 (100 + radius)
// - Top edge: 50 (100 - radius)
// - Bottom edge: 150 (100 + radius)
Drag to move - watch center point
System x: 100, y: 100 means... Rotation pivot
GCanvas (center) Center at (100, 100) Around center
Top-left systems Top-left at (100, 100) Around top-left corner

2. Basic Positioning

When you add an object directly to the pipeline, its coordinates are absolute screen coordinates. Canvas origin (0, 0) is the top-left corner.

Interactive Demo: Screen Coordinates
// Circle centered at screen position (200, 150)
const circle = new Circle(30, { x: 200, y: 150, color: "#0ff" });
this.pipeline.add(circle);

// X increases going right
// Y increases going down
Click anywhere to place shapes

3. Parent-Child Relationships

When you add objects to a Scene, their coordinates become relative to the parent's center. This is the key to understanding hierarchical positioning.

Canvas (0,0)────────────────────────────────►X │ │ Scene at (200, 150) │ ┌─────────────────────┐ │ │ Scene's local │ │ │ origin (0, 0) │ │ │ ●───────────┼──► Scene's X │ │ │ │ │ │ │ Child at │ │ │ │ (50, 30) │ │ │ ▼ ◉ │ │ │ Scene's Y │ │ └─────────────────────┘ │ ▼ Y Child screen position = Scene position + Child local position = (200, 150) + (50, 30) = (250, 180)
Interactive Demo: Parent-Child Coordinates
// Scene centered at (200, 150)
const scene = new Scene(this, { x: 200, y: 150 });

// Child at (50, 30) relative to scene
const child = new Circle(20, { x: 50, y: 30, color: "#0f0" });
scene.add(child);

// Child's screen position: (200 + 50, 150 + 30) = (250, 180)
Drag the scene (green box) - children follow

Nested Scenes

Coordinates stack through the hierarchy:

Interactive Demo: Nested Hierarchy
const outer = new Scene(this, { x: 100, y: 100 });
const inner = new Scene(this, { x: 50, y: 50 });
const shape = new Circle(10, { x: 25, y: 25, color: "#ff0" });

inner.add(shape);
outer.add(inner);
this.pipeline.add(outer);

// Screen position: 100 + 50 + 25 = 175 for both X and Y
Drag any level - nested items follow

4. Camera3D and Scene3D

When using 3D projection, an additional coordinate transformation layer is introduced. Camera3D.project() transforms 3D world positions to 2D screen coordinates centered at the origin.

Key Insight: Camera3D returns coordinates centered at (0, 0). That's why Scene3D must be positioned at canvas center - so the 3D origin appears in the middle of the screen.
Interactive Demo: 3D Projection
const camera = new Camera3D({ perspective: 800 });

// Scene3D MUST be centered for 3D projection to work correctly
const scene = new Scene3D(this, {
  x: this.width / 2,
  y: this.height / 2,
  camera: camera
});

// Object at 3D origin (0, 0, 0) appears at screen center
// Object at (100, 0, 0) appears right of center
Drag to rotate camera - watch projection

The Projection Formula

Step What Happens
1. World Position Object at 3D coordinates (x, y, z)
2. Camera Transform Subtract camera position, apply rotation
3. Perspective scale = perspective / (perspective + z)
4. Screen Offset Returns (screenX, screenY) relative to origin
5. Scene3D Centering Adds (width/2, height/2) to get final position

Common Gotcha: Manual Centering

If you're rendering 3D-projected content outside of Scene3D, you must manually center:

Manual Centering Pattern
// When NOT inside a Scene3D, you must translate to center yourself
Painter.useCtx((ctx) => {
  ctx.save();
  ctx.translate(this.game.width / 2, this.game.height / 2);

  // Now draw at projected coordinates
  const projected = camera.project(x, y, z);
  ctx.fillRect(projected.x - 5, projected.y - 5, 10, 10);

  ctx.restore();
});

5. Anchoring and Layouts

The applyAnchor mixin and layout utilities help position objects relative to the canvas edges or other containers.

Interactive Demo: Anchor Positions
import { applyAnchor, Position } from "gcanvas";

// Anchor text to top-center of canvas
const title = new Text(this, "Game Title", { font: "24px sans-serif" });
applyAnchor(title, {
  anchor: Position.TOP_CENTER,
  anchorMargin: 20  // 20px from top edge
});

// Available positions:
// TOP_LEFT, TOP_CENTER, TOP_RIGHT
// CENTER_LEFT, CENTER, CENTER_RIGHT
// BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT
Resize window to see anchors adjust

Layouts

Layouts automatically position multiple items:

Interactive Demo: Vertical Layout
import { verticalLayout, applyLayout } from "gcanvas";

const items = [
  new Text(this, "Option 1", {}),
  new Text(this, "Option 2", {}),
  new Text(this, "Option 3", {})
];

// Compute vertical layout
const layout = verticalLayout(items, {
  spacing: 15,
  padding: 10,
  align: "center"
});

// Apply positions to items
applyLayout(items, layout.positions);
Click buttons to add/remove items

6. Practical Tips

Quick Reference

I want to... Do this
Place object at screen position Set x, y directly, add to pipeline
Place object relative to parent Add to Scene, set local x, y
Center object on screen x: game.width / 2, y: game.height / 2
Anchor to screen edge Use applyAnchor with Position constant
Use 3D projection Use Scene3D centered at canvas center

Common Mistakes

Mistake 1: Forgetting to center Scene3D
// WRONG - 3D origin appears at top-left
const scene = new Scene3D(this, { x: 0, y: 0, camera });

// CORRECT - 3D origin appears at canvas center
const scene = new Scene3D(this, {
  x: this.width / 2,
  y: this.height / 2,
  camera
});
Mistake 2: Confusing local and screen coordinates
const scene = new Scene(this, { x: 100, y: 100 });
const child = new Circle(20, { x: 50, y: 50 });
scene.add(child);

// WRONG assumption: child is at screen (50, 50)
// CORRECT: child is at screen (150, 150)
Mistake 3: Not updating Camera3D in game loop
update(dt) {
  super.update(dt);
  // If using auto-rotation or inertia, camera needs update
  this.camera.update(dt);
}

See Also