< Back to Rendering Pipeline

Shapes vs GameObjects

Understanding when to use each abstraction in GCanvas

Two Sides of GCanvas

GCanvas serves two distinct use cases with different abstractions. Understanding when to use each is key to working effectively with the library.

Shapes (Drawing)

  • Direct canvas rendering
  • No game loop required
  • Call render() manually
  • Stateless between frames
  • Great for: generative art, static visuals, creative coding

GameObjects (Pipeline)

  • Managed by Game pipeline
  • Automatic update/render cycle
  • Has update(dt) lifecycle
  • Maintains state between frames
  • Great for: games, animations, interactive apps

The Two Hierarchies

Shapes:
Euclidian
Geometry2d
Renderable
Transformable
Shape
GameObjects:
GameObject
Scene
/
Sprite
/
Text
Containers:
Group
holds Shapes
Scene
holds GameObjects

When to Use What

Use Case Use This Why
Static visualization Shape + render() No update loop needed
Generative art Shape, Group Direct control, no overhead
Game character Sprite (GameObject) Needs update cycle for animation
UI elements Text, Scene Pipeline handles positioning
Complex scene Scene with children Hierarchical transforms, lifecycle
Composite shape Group Transform multiple shapes together

Example: Shapes Only (No Game Loop)

You can use GCanvas purely for drawing without any game infrastructure:

import { Circle, Rectangle, Group, Painter } from "gcanvas";

// Get canvas context
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
Painter.ctx = ctx;

// Create shapes
const circle = new Circle(50, { x: 100, y: 100, color: "#0f0" });
const rect = new Rectangle({ x: 200, y: 100, width: 80, height: 60, color: "#0ff" });

// Render directly - no game loop needed
circle.render();
rect.render();

// Group multiple shapes
const group = new Group({ x: 300, y: 100, rotation: Math.PI / 4 });
group.add(new Circle(20, { color: "#f0f" }));
group.add(new Rectangle({ y: 30, width: 40, height: 20, color: "#ff0" }));
group.render();
Shapes rendered directly
This is perfect for one-off drawings, generative art, or when you want full control over the render loop.

Example: GameObjects with Pipeline

When you need animation, input handling, and managed lifecycles:

import { Game, Scene, Sprite, Text, Circle, Keys } from "gcanvas";

class MyGame extends Game {
  init() {
    super.init();

    // Create a scene (GameObject container)
    this.scene = new Scene(this, { anchor: "center" });

    // Create a sprite with animation (GameObject)
    this.player = new Sprite(this, { x: 0, y: 0, frameRate: 10 });
    this.player.addFrame(new Circle(20, { color: "#0f0" }));
    this.player.addFrame(new Circle(25, { color: "#0ff" }));
    this.player.play();

    // Add to scene
    this.scene.add(this.player);

    // Add scene to pipeline - now it's managed
    this.pipeline.add(this.scene);

    // Input handling
    this.events.on(Keys.SPACE, () => this.player.pause());
  }

  update(dt) {
    super.update(dt);
    // Player sprite automatically updates via pipeline
  }
}
Sprite animating via pipeline
GameObjects get their update(dt) called automatically by the pipeline each frame.

Bridging: Shape to GameObject

Sometimes you have a Shape (or Group of shapes) that you want to add to the pipeline. Use ShapeGOFactory:

import { Game, Group, Circle, Rectangle, ShapeGOFactory } from "gcanvas";

class MyGame extends Game {
  init() {
    super.init();

    // Create a Group of shapes
    const avatar = new Group();
    avatar.add(new Circle(30, { color: "#0f0" }));           // head
    avatar.add(new Rectangle({ y: 50, width: 40, height: 60, color: "#0f0" })); // body

    // Wrap it as a GameObject so it can join the pipeline
    const avatarGO = ShapeGOFactory.create(this, avatar, {
      x: 100,
      y: 100,
      interactive: true  // enables click/hover events
    });

    // Now it's a proper GameObject
    this.pipeline.add(avatarGO);
  }
}
Shape Group wrapped as GameObject
Important: Never put GameObjects inside Groups. Group is a Shape container - it won't call update() on its children. Always use Scene for GameObjects.

Quick Reference

Class Type Container Has update()?
Circle, Rectangle, etc. Shape Group No
Group Shape container Group No
TextShape Shape Group No
GameObject Base GO Scene / Pipeline Yes
Scene GO container Scene / Pipeline Yes
Sprite Animated GO Scene / Pipeline Yes
Text Text GO Scene / Pipeline Yes

TL;DR

Just Drawing?

  • Use Shape, Group
  • Call .render() yourself
  • No Game class needed

Building a Game?

  • Use GameObject, Scene, Sprite
  • Add to pipeline
  • Extend Game class