Skip to content

AI images

NPCs and world scripts can generate an image from a text prompt and have it appear on a surface in your world — a billboard, a poster, a screen. Generation runs on the game server (which holds the API key); the finished image is streamed to every player and painted onto the surface you named.

There are three pieces:

  1. An image provider (the service that draws the picture — Grok, OpenAI/DALL·E, or any compatible endpoint).
  2. An SSImageSurface component on the object you want painted.
  3. A trigger — a script call or an NPC action.

1. Set up an image provider

In the Provider Manager (Window ▸ Social Scape ▸ Provider Manager), Add a provider of type Image (gen):

Field What to set
Handle a name you'll reference, e.g. my-images
Provider Grok (xAI), OpenAI (DALL·E / gpt-image-1), or OpenAI-compatible / custom
Endpoint the provider's base URL, e.g. https://api.openai.com or https://api.x.ai — a bare host, a /v1 base, or a full /v1/images/generations path all work (the path is normalized for you, so no /v1/v1 mistakes)
Model e.g. gpt-image-1, dall-e-3, grok-imagine-image-quality
API Key stored encrypted on your account (blank for a no‑auth/self‑hosted endpoint)

Then on the NPC's SSAINpc, set its Image Provider dropdown to that handle (it also becomes the world's default image provider). Self‑hosted / no‑auth endpoints work with no key.

Custom & self-hosted endpoints

Anything that speaks the OpenAI /v1/images/generations contract works — pick OpenAI-compatible / custom and point the endpoint at your server.

Cloud keys never ship in your world

The API key is stored encrypted on your SDK account, not in the world bundle — exactly like your text/voice keys (Providers & keys). The world carries only the handle; the server matches the key at runtime.

Test it before you upload

Open the AI NPC Creator Tester (menu Social Scape ▸ AI NPC Creator Tester) and expand Image Test (generation). Pick the vendor, paste your endpoint/model/key, type a prompt, and hit ▶ Generate — it calls the endpoint straight from the editor and previews the result (with Save PNG), so you can confirm the provider works before building and uploading a world.


2. Mark a surface

Add an AI Image Surface component (Add Component ▸ Social Scape ▸ AI Image Surface, or SSImageSurface) to the object you want painted:

Field Meaning
Surface Key the name you'll target, e.g. billboard. Must match the key you pass when generating.
Target Renderers Renderers whose material gets the image (quads/planes/meshes).
Target Sprites (optional) SpriteRenderers to receive the image.
Target Raw Images (optional) UI RawImages (a Canvas/panel) to receive the image.
Use Instanced Material ON (default) paints a private copy so other objects sharing the material aren't changed.

Each target is a list — list as many objects as you like (any mix of the three types) under one surface key, and a single generate call paints them all at once (a scene‑wide set of billboards/screens from one request). Leave every list empty to just paint the object the component sits on.

Capped at 50 per type

Each list is hard‑capped at 50 entries. Extra entries are dropped — the cap is enforced by the platform when the world loads, so it can't be exceeded.

No material needed

If a target Renderer has no material (a bare Quad), one is created for you automatically — a simple unlit material that shows the image as‑is. You only need to assign a material if you want a specific look. (Building a standalone world? Make sure an unlit shader ships in the build — add one under Project Settings ▸ Graphics ▸ Always Included Shaders — so the auto‑created material always renders.)

A simple recipe: a Quad facing the player, with an SSImageSurface whose surfaceKey is billboard. For a wall of screens, add all their Renderers to Target Renderers under the same key.


3. Generate an image

3a. From a script

Paint a surface directly — fire-and-forget; the image appears when it's ready:

// onto a surface (uses the SSImageSurface with surfaceKey "billboard")
AI.GenerateImageOnto("billboard", "a neon dragon poster on weathered paper");

// optional aspect ratio: 1:1 (default), 16:9, 9:16, 4:3, 3:4
AI.GenerateImageOnto("wall", "a calm mountain sunset", "16:9");

Prefer to place the texture yourself? Generate under a key, then read it back when ready:

AI.GenerateImageOnto("dragon", "a coiled neon dragon");

// later (e.g. in Update, or after a delay):
if (AI.IsImageReady("dragon"))
    myRenderer.material.mainTexture = AI.GetTexture("dragon");
Call Returns Notes
AI.GenerateImageOnto(key, prompt [, aspect]) bool (started) generates + delivers; paints any SSImageSurface with that key
AI.IsImageReady(key) bool true once the image has arrived on this client
AI.GetTexture(key) Texture (or null) the decoded texture, to assign anywhere (material.mainTexture, …)

3b. From an NPC

So an NPC can draw on request ("paint the billboard with a sunset"), give it the built-in Generate Image action in the Action Designer:

Parameter Value
surface the surfaceKey to paint, e.g. billboard
prompt what to draw (let the AI fill this, or fix it)
aspect (optional) 1:1, 16:9, …

The NPC can then decide to call it mid-conversation, and the image lands on that surface for everyone.


A single surface shows one image (the latest replaces the last). For a chat panel / gallery that keeps adding images — an NPC's sketchbook, a moodboard, a "here's everything I drew" wall — use a feed.

  1. Add an AI Image Feed component (SSImageFeed) to your panel:

    Field Meaning
    Feed Key the key you append to, e.g. gallery
    Content the container rows are added under (e.g. a ScrollView's Content); defaults to this object
    Row Prefab instantiated once per image — give it a UI RawImage, a SpriteRenderer, or a Renderer to receive the texture
    Max Items rows kept on screen (older ones drop off); 0 = unlimited
    Newest First add new images at the top instead of the bottom
  2. Append images to it:

    AI.AppendImageToFeed("gallery", "a watercolor fox");   // adds a new row
    AI.AppendImageToFeed("gallery", "a watercolor owl");   // adds another
    

Each call adds a row. A player who joins later gets the last N images rebuilt in order — not just the most recent. (Server‑side the feed keeps a bounded history, so memory stays in check no matter how long it runs.)

Build a feed in script (no component)

Prefer to lay the gallery out yourself? Read the feed straight from script and apply each image wherever you like:

int n = AI.GetFeedCount("gallery");        // images that have arrived (oldest → newest)
for (int i = 0; i < n; i++)
{
    Texture t = AI.GetFeedTexture("gallery", i);   // i = 0 is the oldest
    // … assign t to your own row's RawImage / material, position it, etc.
}

AI.GetFeedTexture returns the same textures a late joiner receives, so rebuilding your UI from 0…GetFeedCount-1 on join reproduces the whole gallery.


Script API reference

All of these run client‑side except AI.GenerateImageOnto / AI.AppendImageToFeed, which kick off generation on the server.

Call Returns What it does
AI.GenerateImageOnto(key, prompt [, aspect]) bool generate → paint the surface(s) with that key (and cache under key)
AI.AppendImageToFeed(feedKey, prompt [, aspect]) bool generate → append a new image to the feed
AI.IsImageReady(key) bool true once a surface image has arrived on this client
AI.GetTexture(key) Texture the surface texture for key (null until ready)
AI.GetFeedCount(feedKey) int images in the feed (oldest→newest)
AI.GetFeedTexture(feedKey, index) Texture the feed image at index (0 = oldest), or null

Good to know

  • Everyone sees it. The server streams images to all players in the instance, so surfaces and feeds look the same for everyone.
  • Late joiners catch up. A player who joins after an image was generated does get it: the current image on each surface, and the last N images on each feed, are replayed once they load in. (Feeds are bounded, so very old images past the cap aren't kept.)
  • Cooldown. Repeated generations for the same key/feed are throttled (a few seconds apart) so an NPC can't spam an expensive endpoint.
  • Size cap. Very large images (over ~8 MB) are dropped — keep resolutions reasonable (1024² is plenty).
  • Memory cap. Each client keeps at most 255 generated images in memory at once (oldest evicted) — so a long‑running gallery can't grow unbounded.
  • It costs you. Image generation bills your provider account and takes a few seconds; trigger it on purpose, not every frame.