Personalized World Models with Reactor

Use an Onairos persona to seed real-time, walkable worlds with Reactor's world model.

Updated 30 June 2026
reactorworld-modelspartnerpersonalizationlingbot
Reactor ×

Onairos has partnered with Reactor to make generated worlds personal by default. Together we turn personalized world models into something simple and magical to build.

A full flow demo of Onairos personalization + Reactor's world model
A user's daydream shifting from one aesthetic scene to the next

How the pipeline works

Three pieces make the interface work:

  1. Onairos brings the user-owned context (the persona).
  2. An image model (e.g. Gemini) renders that context into a seed frame.
  3. Reactor turns the frame into a live, walkable world.

Only one step is yours to design: how you translate a persona into a world. What that world is — a travel scene, a game level, a storefront, a gallery, a calm space to think — is entirely up to your product. Everything else (reading the profile, seeding the frame, and spawning the world) stays the same.

Key principle
Reactor never sees the raw persona. By the time the world generates, the persona has been distilled into just two things: a seed frame and a scene prompt. Put your strongest signal — the archetype, top traits, and what to exclude — into the scene before it is rendered, so it gets baked into the frame.

Conventions for these examples

Some names below are real APIs you import and call. The rest are helpers you write yourself — placeholders standing in for your product's own logic.

Real APIs — import and call these
  • OnairosButton, initializeApiKey — from the onairos SDK.
  • LingbotProvider, useLingbot(), useLingbotImageAccepted() — from Reactor's @reactor-models/lingbot SDK. useLingbot() returns uploadFile, setImage, setPrompt, setSeed, start, setMovement, setLookHorizontal, and setLookVertical.

The Seed Forge route shown in Step 3 is an internal reference, not a public endpoint — you go through your own forgeSeed helper, which calls it server-side.

Helpers you write — placeholders, not SDK functions
  • extractProfile, translateToScene, forgeSeed, captureWorldFrame, yourMatcher — your product's own logic; name and implement them however you like.
  • ReactorWorldProvider — a thin wrapper you write around Reactor's LingbotProvider.
  • waitImageAccepted — a one-line promise wrapper around the useLingbotImageAccepted() hook, so you can await the event inline.

1. Read the persona

A few lines drop the Onairos consent flow into your app. When the user connects, onComplete hands you their permissioned profile.

import { OnairosButton, initializeApiKey } from 'onairos';

await initializeApiKey({ apiKey: process.env.NEXT_PUBLIC_ONAIROS_API_KEY });

<OnairosButton
  webpageName="YourApp"
  requestData={['basic', 'personality']}
  trainingScreenMode="fast"
  onComplete={(result) => buildWorld(extractProfile(result))}
/>

A user's Onairos profile is a plain object you can shape a scene from:

// extractProfile(result) →
{
  archetype: "The Quiet Cartographer",         // one-line identity
  summary:   "Drawn to depth over breadth, …", // a paragraph of nuance
  topTraits: [{ name: "Curious Exploration", score: 92 }, …], // scored positives
  lowTraits: [{ name: "Thrill-Seeking",      score: 18 }, …], // what to avoid
  mbti:      "INFP",                           // light energy / social signal
  platforms: ["spotify", "youtube", "reddit"]  // where the read came from
}

2. Translate the profile into a scene

This is the part you design. Turn the profile into a scene prompt — a description of the world to generate. How you map a persona to a world is your product's own logic, so the same pipeline can power very different apps.

// your logic: map an Onairos persona → a scene for the world
const scene = await translateToScene(profile);
// → { prompt, ...anything else your product needs }
// e.g. attach a search query, a level id, a product category…

Not every field matters equally. Here is what each one drives:

FieldWhat it shapes
topTraitsThe base reality: the kind of world, its mood and pace. Highest scores dominate.
lowTraitsThe base reality by exclusion: what to keep out, like thrill, crowds, or danger.
archetypeThe anchor: sets the genre of the world before any detail.
summaryAugmentation: the specific objects and textures once the world's kind is set.
mbtiA light nudge on energy and social density.
platformsWhich data sources the persona was read from.
Rule of thumb

Traits set the base reality — the kind of world and what to keep out. The summary fills in the details once that world exists. The archetype is the anchor that ties it together.

3. Plant the seed frame

Reactor's world model needs a seed frame, so the scene is handed to an image model and rendered into a single still — the frame the world grows from.

// scene prompt → one still image (a Blob), the frame the world grows from
const seed = await forgeSeed(scene.prompt);

Under the hood, forgeSeed posts the scene to a server-side "Seed Forge" route and names the image model to use. The provider call stays server-side, so no image-model key ever touches the client. The shape below is for reference only — it shows what your forgeSeed helper sends; it isn't a public endpoint you call directly.

# reference only — reached through your forgeSeed helper, not called directly
POST /reactor/seed-forge
{
  "prompt":        scene.prompt,
  "imageProvider": "gemini",
  "imageModel":    "gemini-3.1-flash-image",
  "aspectRatio":   "16:9",
  "imageSize":     "2K"
}
// → streams back the rendered frame as an image Blob

Swap imageProvider or imageModel to render the seed with a different image model — the rest of the pipeline doesn't change.

4. Hand it to Reactor

The Reactor provider mints a short-lived JWT and opens the WebRTC connection for you, so you never manage sockets. From there it's a short handshake: upload the seed, wait for the model to lock onto the frame, set the prompt, then start.

// inside <ReactorWorldProvider> — your wrapper around LingbotProvider (auto-connects + handles auth)
const { uploadFile, setImage, setPrompt, setSeed, start } = useLingbot();

const ref   = await uploadFile(seed);      // upload the seed frame → a handle
const ready = waitImageAccepted();         // wraps useLingbotImageAccepted() → a promise; arm before setImage
await setImage({ image: ref });
await ready;                               // model has locked onto the frame
await setPrompt({ prompt: scene.prompt }); // how the world unfolds as you move
await setSeed({ seed: 42 });              // pin the RNG → reproducible world
await start();                            // live: WASD to move, look to explore
  • Order matters. Subscribe with useLingbotImageAccepted() (here wrapped as waitImageAccepted()) before setImage, or you can miss the event. Prompt and seed are set after the frame is accepted, then start().
  • The seed is a frame, not text. setImage is the heavy lift — it's what the world grows from. setPrompt steers how it unfolds as you move.
  • Output is a live video track rendered by the SDK's view component. Movement and look are ongoing commands (setMovement, setLookHorizontal, setLookVertical) and the world reacts in real time.

5. (Optional) Connect the world to your product

The live world is a real-time video the user moves through. At any moment you can capture the current frame and combine it with the persona to drive whatever comes next in your app. This step is entirely yours — Reactor just gives you the live view to build on.

const frame = captureWorldFrame(); // the current live view

// feed the frame + persona into your own product logic
const results = await yourMatcher({ profile, image: frame });
// travel → destinations · commerce → products · media → titles · games → quests

Because it is your logic, you decide what "next" means — surfacing matches, saving state, or triggering an action.

Why Reactor + Onairos

Reactor builds the world, Onairos makes it personal, and together they turn personalized world models into something genuinely simple to build. This is the first step toward world models that are personal by default.

Building something dope with this? Ship it and tag @reactorworld and @onairosapp on X — we'll help get eyes on it. Now go build something worth showing off. Good luck. 🚀

← Back to Web Integration