Skip to content

Build your first world

By the end of this tutorial you will have a new world that appears in the navigation, the search, and its own route, rendering a simple scene. We will add a minimal “Sky” world to keep the focus on the wiring rather than the physics.

Everything starts in lib/worlds.ts, the single source of truth. Add a row to the WORLDS array.

  1. Add the id to the WorldTab union so it is type-checked everywhere.

    lib/worlds.ts
    export type WorldTab =
    | "earth"
    // ...existing ids...
    | "sky";
  2. Add the world’s row. Keep the blurb honest: describe exactly what the view renders.

    lib/worlds.ts
    {
    id: "sky",
    label: "Sky",
    href: "/sky",
    group: "earth",
    blurb: "A plain gradient sky, as a minimal starter scene.",
    accent: "#7fb2ff",
    keywords: ["sky", "starter", "example"],
    },

The registry has invariants (unique ids, unique hrefs, counts). Update lib/worlds.test.ts so the counts match, then run the tests.

Terminal window
npm run test -- worlds

Add the App Router page. Worlds follow a {World}Shell to {World}App to {World}Scene pattern, but a starter can be a single client component.

  • Directoryapp/
    • Directorysky/
      • page.tsx
app/sky/page.tsx
import NavShell from "@/components/ui/NavShell";
export default function SkyPage() {
return (
<main className="h-dvh w-dvh bg-gradient-to-b from-[#0b1a3a] to-[#7fb2ff]">
<NavShell active="sky" onAbout={() => {}} />
</main>
);
}

Run the dev server and open the app. Your world is already in the navigation and the command palette (press Cmd/Ctrl+K and type “sky”), because both read from the registry. Visit http://localhost:3000/sky.