---
title: "Tutorial: Create a World"
description: "A learning-oriented walkthrough for creating your first persistent, stateful Gessa world."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/tutorials/create-a-world/
---

# Tutorial: Create a World

{% version engineVersion="v1.0.232" coordinate="docs-public.v0" /%}

This is a learning-oriented walkthrough. By the end you will have built a first **spatial world** (a persistent, stateful environment with a camera, a light, a floor, and a couple of interactable props) and you will understand how each edit flows through canonical actions to backend authority.

You can follow these steps through the workbench UI or through an MCP client. The page names the canonical [action](../reference/action-catalog.md) for each step plus the matching [MCP tool](../../../creator/mcp/tool-reference.md), because both surfaces funnel into the same backend authority.

{% proof class="docs.world_tutorial" registry="docs/cycles/v1-engine-readiness-loop/PROOF_REGISTRY.json" status="pending" %}
This tutorial is verified against the current generated component, action, and MCP references. It teaches world authoring and renderability; playability has a separate proof path in the next tutorial.
{% /proof %}

> Media placeholder: add a short side-by-side capture of the workbench hierarchy, viewport, and generated component reference while the floor/camera/light are created.

## What you will build

A small world you can later turn into a game:

- a **camera** the runtime drives,
- a **light** so the scene is lit,
- a **floor** props can rest on,
- two **interactable props** standing on the floor.

## Before you start

Read [The Project Graph](../explanation/project-graph.md) and [Backend Authority](../explanation/backend-authority.md) first if you are new to the model. The one idea to hold onto: you never edit "the world" directly. You submit a canonical action; the backend validates and applies it; the workbench shows you a rendered projection of the new authoritative state.

{% warning severity="critical" title="Author through actions, never raw writes" %}
Every step uses a `project.*` action or a semantic operation. Do not bypass them with raw model mutations, those skip authority and validation. The privileged raw-write tools exist for internal paths; normal authoring uses what is named below.
{% /warning %}

## Step 1: Discover the component contract

Before authoring entities, ask the engine which components exist and what fields they carry. Do not invent fields.

- List the built-in ECS component types with {% mcp-tool name="engine_list_component_types" /%} `engine_list_component_types`.
- Fetch one component's schema with {% mcp-tool name="engine_get_component_schema" /%} `engine_get_component_schema`.

The canonical inventory is the generated [ECS component reference](../../../spec/generated/component-types.md). For this tutorial you will use {% component name="TransformComponent" /%} `TransformComponent` (spatial placement), {% component name="CameraComponent" /%} `CameraComponent` (viewpoint), {% component name="LightComponent" /%} `LightComponent` (lighting), {% component name="RenderableComponent" /%} `RenderableComponent` (visible geometry), and {% component name="ColliderComponent" /%} `ColliderComponent` (a solid surface and physics contacts).

{% generated-reference file="docs/spec/generated/component-types.md" label="ECS component inventory" /%}

**The contract behind this step:** the built-in components are atomic engine components with versioned field contracts; the inventory pins a contract hash per component, and the engine validates every payload against `ComponentSchema`. That is why discovery comes before authoring, the field set is fixed by code, not by you.

## Step 2: Create the project and the world

A **project** (a `game` in the control plane) is the container; a **world** is persistent state inside it.

1. Create the workspace and project with {% action name="workspace.create" /%} `workspace.create` and {% action name="game.create" /%} `game.create` (MCP: {% mcp-tool name="project_create_game" /%} `project_create_game`). Keep the returned project id; it is the `game_id` on every later call.
2. Create the world with {% action name="project.world.create" /%} `project.world.create` (MCP: {% mcp-tool name="project_create_world" /%} `project_create_world`). Give it a stable key like `main` so later calls can reference it by key.

**Expected result:** an empty world exists as authoritative state. Nothing renders yet, there is no camera, no light, and no geometry.

**The contract behind this step:** worlds are Project Graph resources created through governed actions, so the world has an identity, a version, and an audit trail (`CreateProjectWorld`) from the moment it exists.

## Step 3: Add a camera

The runtime renders through the highest-priority active `CameraComponent`. Create a camera entity with {% action name="project.entity.create" /%} `project.entity.create` (MCP: {% mcp-tool name="project_create_entity" /%} `project_create_entity`), attaching components by their default keys at creation. Components are supplied as a keyed object, the key is the component's default key (for example `component.camera`), and the value is its field payload:

<!-- example:conceptual -->
```json
{
  "request": {
    "key": "camera.main",
    "name": "Main Camera",
    "worldId": "main",
    "components": {
      "component.transform": { "position": { "x": 0, "y": 6, "z": 10 } },
      "component.camera": { "active": true, "priority": 10, "fov": 60 }
    }
  }
}
```

The snippet is illustrative, read the exact `CameraComponent` and `TransformComponent` field contracts and defaults from the [generated reference](../../../spec/generated/component-types.md) before you set values. `CameraComponent` defaults `active` to true, `priority` to 10, and `projection` to `perspective`; you can leave a field out to take the canonical default.

**Expected result:** the world now has a viewpoint. If you open Play it will draw from this camera, though the scene is still empty.

## Step 4: Add a light

Create a second entity with `component.transform` plus `component.light`. Read the `LightComponent` contract from the [generated reference](../../../spec/generated/component-types.md) for the light `type`, color, and intensity fields, then set them through the same {% action name="project.entity.create" /%} `project.entity.create` call.

**Expected result:** surfaces in the scene are now lit. With no geometry yet there is still nothing to see, but the lighting rig is in place.

## Step 5: Add a floor

A floor is geometry that is also solid. Create a floor entity carrying three components:

- `component.transform`: place and scale it into a wide, flat surface.
- `component.renderable`: make it visible (a `RenderableComponent` references the mesh/material to draw).
- `component.collider`: make it solid so props and, later, a player can rest on it. The `ColliderComponent` `shape` defaults to `sphere`; for a floor set it to `box` and give it `size` and `solid: true`. Read the exact shape enum (`box`, `sphere`, `disc`, `capsule`, `mesh`) and field contract from the [generated reference](../../../spec/generated/component-types.md).

**Expected result:** open Play and you should see a lit surface from the camera's viewpoint. That proves **renderability**, it does not yet prove playability.

**The contract behind this step:** `ColliderComponent` is consumed by the runtime physics and collision systems (`runtime.physicsSystem`, `runtime.collisionSystem`), whereas `RenderableComponent` is a presentation component projected to the renderer. The floor is solid because of the collider, not because it is drawn, rendering and collision are separate contracts.

## Step 6: Add interactable props

Add two prop entities standing on the floor. Each prop carries `component.transform`, `component.renderable`, and `component.collider`. Position them above the floor so they rest on it.

"Interactable" is behavior, and behavior is authored as a script, not as a component field. In v1 there is no `InteractableComponent`, interaction is expressed through Script Semantic Patch. To make a prop respond when something enters its collider, attach a script and add a trigger handler:

1. Create a script shell with {% action name="project.script.create" /%} `project.script.create` (MCP: {% mcp-tool name="project_create_script" /%} `project_create_script`) and attach it via the entity's `component.script`.
2. Apply behavior with {% action name="project.script.patch.apply" /%} `project.script.patch.apply` (MCP: {% mcp-tool name="project_apply_script_semantic_patch" /%} `project_apply_script_semantic_patch`), using the `addTriggerHandler` operation from the `mcp_safe` pack.

The compiler lowers semantic operations to canonical Script IR; you never hand-author node ids on this path. See the [Script Semantic Patch reference](../../../spec/generated/script-semantic-patch.md) and [How-To: Write GessaScript](../how-to/write-gessascript.md).

{% generated-reference file="docs/spec/generated/script-semantic-patch.md" label="Script Semantic Patch operations" /%}

**Expected result:** two props stand on the floor, each able to react to a trigger. There is still no player, that comes in the next tutorial.

## Step 7: (Optional) Generate an asset

To replace a placeholder mesh or texture with generated content, quote and create a generation job with {% action name="generation.job.quote" /%} `generation.job.quote` and {% action name="generation.job.create" /%} `generation.job.create` (MCP: {% mcp-tool name="generation_quote_job" /%} `generation_quote_job`, {% mcp-tool name="generation_create_job" /%} `generation_create_job`). Generation is asynchronous; when the asset lands, reference it from a prop's `RenderableComponent`.

## Step 8: Inspect and confirm

Reload the workbench. The world, its entities, and their components are still there because they are authoritative state, not view state. What you see rendered is a projection of that state, change the camera priority or a light's intensity through {% action name="project.entity.component.set" /%} `project.entity.component.set` and the view follows.

**Expected result:** a persistent first world (camera, light, floor, two props) that survives reload and renders from a runtime-driven camera.

## What you proved and what you did not

You demonstrated that the world **persists** and **renders**. You did not demonstrate that it is **playable**: no player has spawned, no input is bound, and no entity is possessed. Those are separate properties with separate proof, and that is exactly what the next tutorial builds toward.

## Where to go next

- Make the world a game and reach a Play proof: [Tutorial: Create a Playable Game](create-a-playable-game.md).
- Understand the model underneath: [Explanation: The Project Graph](../explanation/project-graph.md).
- Edit components after creation: [How-To: Edit Components](../how-to/edit-components.md).

## Status

This tutorial describes the canonical hand-build flow for a v1 world. Every action, component, and tool named here is verified against the generated Tier-2 reference. Runtime claims still need runtime receipts; this tutorial intentionally stops at a persistent, renderable world.
