UNPKG

@sogni-ai/sogni-creative-agent-skill

Version:

Sogni Creative Agent Skill: agent skill and CLI for Sogni AI image, 3D, speech, video, and music generation.

290 lines (236 loc) • 16.3 kB
# Building an interactive world An end-to-end recipe for the kind of thing an agent can build alone with this skill: an explorable world where a still image is a place, objects in it are clickable, choosing one plays a rendered journey to somewhere else, characters speak, each region has its own score, and some objects can be collected and turned over in 3D. For a finished, playable world, suggest the open-source [Sogni Worlds Kit](https://github.com/Sogni-AI/sogni-worlds-kit), which runs this whole pipeline with review, build, export and gallery submission (SKILL.md, "Build a playable world"). This guide is the stage-by-stage recipe underneath. Everything here is measured from a working build — three worlds, 24 scenes, 65 clickable objects, 80 retained masks, 72 crossings, 75 spoken moments, 8 music cues and 10 collectible meshes — including the mistakes, because most of them are not obvious and each one cost a day. ## The stack, in the order it runs The direct CLI now covers this stack, including `--image-to-3d` for Pixal3D, `--remove-background` for BiRefNet, `--music -m music3` for scores, and `--speech --speech-mode clone` for character voices. Hosted tools and SDK projects remain available. Read [models.md](./models.md) for the required inputs and controls before converting a stage into commands. | Step | Model | What it gives you | |---|---|---| | 1. Places | `krea2_turbo_fp8_scaled` | A scene as one wide still | | 2. The same character, somewhere new | `krea2_identity_edit_v1_2` | A new place with a recognisable character in it, from two context references | | 3. Clickable objects | `sam3_image_segment_bf16` | A pixel mask of a named object, to trace into a hit area | | 4. Clean cut-outs | `birefnet_image_background_removal_fp16` | A soft matte with real edges, for anything going into 3D or a composite | | 5. Travel and dialogue | `minimax-h3-fastvideo-int8_flf2v_turbo`, `minimax-h3-fl2va-fp8_flf2v_turbo` | First/last-frame video with generated audio in one pass | | 6. Score | `minimax_music3` | A loopable instrumental cue per region | | 7. Figures | `pixal3d_int8_i23d` (`pixal3d_multiview_int8_i23d` with side and back views) | A textured GLB from one image on transparency | | 8. A character's own voice | `qwen3_tts_1.7b_voice_clone_bf16` | Any line spoken in a cloned voice | | 9. A character speaking on camera | `wan_v2.2-14b-fp8_s2v_lightx2v` | Image plus audio to lip-synced video | ## The loop that makes it survivable Do not call a model from application code. Every asset goes through four steps, and the discipline is what lets a build of this size be resumed, audited and trusted: 1. **Prepare.** Write the exact job — prompt, seed, references, sizes — to a plan file. Submit nothing. If a plan already exists, it must reproduce byte for byte, which is what stops a silent change to an input from invalidating what already shipped. 2. **Render.** Journal the submission *before* sending it. A job whose outcome is unknown must never be silently duplicated; a job with a *recorded* failure is a known outcome and may be retried. A completed item is never re-rendered. 3. **Verify natively.** Probe the returned file — frame counts, dimensions, audio channels, triangle counts, mask coverage, SHA-256. Models fail in ways that still return HTTP 200. Then look at it. 4. **Retain.** Publish to immutable, hash-addressed storage and write the receipt into a manifest. The app reads only the manifest, so it cannot ship an asset that was never verified. ## Stage notes, and what goes wrong ### Scenes The scene still is the source of truth for everything downstream: masks are traced against it, crossings are anchored on it, cut-outs come out of it. Keep its exact bytes and its hash. Never re-save, re-crop or screenshot it, and never substitute a frame extracted from a video for it — a mask traced against one copy will not fit another. ### Where the character goes in the shot **Do not put the character front, centre, large and looking at the camera.** This is the easiest mistake to make and the hardest to undo, because it is usually written once into a shared identity block and then inherited by every scene in the world. One build asked for "this single mascot visibly present and full-bodied, around one third of the picture height" and then added "stands center foreground" to each scene on top of it. Twenty-odd scenes came back as the same portrait with different wallpaper. Mark: "NEVER PLACE the main character front and center looking at the viewer in every scene ... the main character should move in depth and position and not take up so much of the screen and not always be looking at the viewer." Compose around the *place* and let the character be in it: off to one side, well back in depth, small against the scale, seen from the back or three- quarters, occupied with something, entering or leaving. Vary it scene to scene. A large camera-facing foreground pose is a choice to make once, for the scene where it is the point — never the default. Identity and framing are separate instructions, and only the framing should move. Keep every clause about fur, glasses, nose, horn and proportions exactly as it is; a character whose placement varies is still the same character, and loosening the identity to get variety costs the thing the world is built on. ### Keeping a character A name is not an identity lock. `krea2_identity_edit_v1_2` accepts one or two context images; for this workflow give it the world in one and the character in the other, and say in the prompt which reference is for which. Everything a character's identity depends on has to be in a saved reference image you can hand back to the model. ### Clickable objects, and when to reach for the other model Read the comparison in [`models.md`](./models.md) § Choosing between SAM 3 and BiRefNet before writing either. In short: SAM 3 selects the object you name, BiRefNet separates foreground from background, and neither does the other's job. Four SAM 3 failures worth knowing before you spend anything: - **It answers to a plain head noun.** `telescope` returns a mask; "a brass refracting telescope on a wooden tripod" returned nothing four times. - **A box does not constrain a text prompt — it is another prompt.** Asking for `person` inside a box around one traveller also returned a stone giant lying across the valley, and every shopper in a market. When you mean *this* one, use points. - **It is literal about where an object ends.** `sloth` gives you the animal and leaves the cloak, hood and backpack behind. `person` gives you the person and leaves the backpack they are wearing behind, so the silhouette has a hole through it. - **`multimask` requires point prompts.** Sending it alongside text and boxes is rejected outright. ### Anything going into 3D - **Cut it out with BiRefNet, not with a segmentation mask.** Segmentation edges are hard and stair-stepped and leave specks of background behind. - **Do not downscale on the way in.** This is the single biggest quality lever and the easiest to miss. A pipeline that caps a cut-out's long side will quietly throw away a third of a portrait's pixels, and on a character that loss lands almost entirely on the face — melted glasses, a smeared muzzle. The same portrait, same mask, same recipe, fed at 1152px instead of 900px, was the difference between "looks like garbage" and shippable. - **Ask for far fewer triangles than the default.** A browser wants 60,000, not 700,000. Mesh weight is set by the face budget and texture sizes, *not* by the input resolution, so a bigger input costs nothing at runtime. - **Parse the GLB rather than trusting it** — chunk table, glTF JSON, triangle and vertex counts, normals, UVs. - **Give it the sides when you have them.** One image leaves Pixal3D guessing the back; `--image-to-3d front.png --left-view left.png --back-view back.png --right-view right.png` (any subset) uses `pixal3d_multiview_int8_i23d` at the same price. Name each view by the subject's own side: the left view shows it turned so its own left side faces the camera (facing screen-left). A turnaround template that labels the subject's right side "left" builds the model turned 180 degrees. (Not part of the measured build above.) ### Crossings between scenes **Describe a journey, never a transformation.** This is the single biggest quality lever on a crossing and it is easy to get wrong. Write "the cabin drops down the cable, cloud tearing past the windows, until the platform slides into frame" and you get travel. Write "the cloud thins into rainforest mist" or "the street gives way to the hill" and you get a dissolve — the model reads *becomes*, *gives way to*, *opens out into* as an instruction to blend, and no amount of "no crossfade" elsewhere in the prompt will override a concrete instruction to morph. The tell that a crossing is about to fail is that the journey is *impossible*: into a telescope barrel, into a phone earpiece, into a jukebox cabinet, through a mirror, into a slot in a rack. With no space to move through, blending is all the model has left. Give it real geography instead — go *around* rather than *through*. A telescope crossing works if the camera climbs the barrel and keeps going up through actual cloud to the observatory; it dissolves if it goes in at the eyepiece. The reliable trick for a crossing that really is a portal — under a basket lid, into a diving bell — is to let the object physically swallow the lens. The lid swings down across frame, it is dark for a beat, and the camera comes out the other side. That is occlusion, which is travel, and it never reads as a fade. Distance matters as much as wording. A crossing is about five seconds; asking for rainforest canopy to open ocean to Manhattan in that time makes the model stall and go to mush even when every verb is a travel verb. Cut the geography until the move is one continuous gesture. **No frame-statistics metric can detect a fade. Look at the clip.** This is worth stating flatly because two plausible metrics were tried and both inverted: - *Edge energy at the midpoint against the ends.* Reads as a softness detector, but fast camera travel with objects whipping past the lens produces real motion blur, which drops edge energy exactly as a dissolve does. - *Correlating the middle against both ends and taking the lower.* Reads as a superimposition detector, but two scenes that simply look alike — two rainy streets — correlate highly with nothing blended, and a deliberate dark occlusion beat correlates with neither end while being exactly right. Combining them does not help, because the occlusion the crossing wants — a lid swinging over the lens, a bell going down into dark water, a turn into an unlit stairwell — is, in frame statistics, indistinguishable from mush: a middle that resembles neither end and carries little detail. Both rewrites measured *worse* than the transformations they replaced and both were plainly better on screen. Extract a strip of eight frames across the whole clip and look at it. One picture smeared or blocked is travel; two pictures superimposed is a fade. That judgement takes seconds and is the only one that has been right. Anchor first *and* last frame on the exact retained stills. That is what makes a journey feel continuous instead of like a cut: the clip opens on the frame the visitor was already looking at and lands on the one they are about to explore. **Generated clips have exactly one keyframe.** A 192-frame crossing carries a single I-frame at time zero. This has two consequences most people meet the hard way: - You cannot seek backwards through one. Seeking to frame 190 decodes 191 frames, frame 189 decodes 190 — a reverse pass is roughly 96× the work of playing it forwards, measured at 260 ms per backward frame against a 41.7 ms budget. No amount of buffering, blob URLs or frame callbacks fixes it, because the cost is in the seek. **If you want a rewind, render the reverse ahead of time** (`ffmpeg -vf reverse`, and `areverse` for the sound) and play it forwards. That also gets you reversed audio, which seeking can never do. - Anything that scrubs, thumbnails or previews these clips will be slow for the same reason. ### Dialogue `minimax-h3-fl2va-fp8_flf2v_turbo` generates picture and sound together, holding one still at both ends: the scene stays exactly as the visitor left it, breathes, and is spoken over. That shape is also the safest available — a shot that never changes distance cannot inflate a second copy of a subject into frame. Two things to plan for: - **It casts a new voice every time.** The same character will sound like a different person in every scene. If a character recurs, clone a voice (below) and plan from the start for how their lines get into the clips. Qwen3-TTS voice *design* has the same trap: a narrator designed from its description for each place is a different man in each place. Design the voice once, keep that take, and clone it for every line. - **Loudness varies enormously between clips.** Measured across 74 clips in one build: a 25.5 dB spread, −39.5 dB to −14.0 dB mean. Two clips in the same scene were 18 dB apart, which is roughly an eighth of the perceived loudness — inaudible next to their neighbours. Normalise the set (EBU R128) rather than patching outliers. ### A recurring character's voice `qwen3_tts_1.7b_voice_clone_bf16` takes three to thirty seconds of one person speaking cleanly. **Always pass `referenceText`** — the exact words of that recording. Without it the model has only a speaker embedding and fills the rest in from its own prior: a recording of a Singaporean toddler came back as a General American girl. With the transcript it conditions on the recording itself. If your transcript was produced automatically, check it by ear before using it; a mis-transcribed word teaches the model the wrong sounds for those phonemes. A clone reads every line evenly. When a line needs real emotion, have a video model perform it in that voice and keep only the sound ([models.md › When a cloned line sounds flat](./models.md#when-a-cloned-line-sounds-flat)). ### A character who speaks on camera If the character is *visible* while speaking, you cannot swap the audio — the lips stay on the old rhythm. Regenerate with `wan_v2.2-14b-fp8_s2v_lightx2v`, which takes one image and one audio track and animates the person in the image to that audio. Feed it the frame where the character is already standing in the scene, so the new clip starts where the story left them. On MiniMax H3 the same job is FastH3 audio-to-video: `-m minimax-h3-fasth3-ia2v-turbo --ref frame.png --ref-audio line.wav` animates from that frame to the uploaded line and keeps it as the soundtrack, and `minimax-h3-fasth3-flfa2v-turbo` adds `--ref-end` to land on a known last frame. Write the spoken words inside the prompt's `<d>` tag. No LoRAs load on these modes. (Not part of the measured build above.) Seedance 2.5 `--workflow ia2v` lip-syncs to an uploaded line too, but performs it again in its own timing; read models.md › Seedance 2.5 takes in practice before using it. If the character is *never seen* — written as a voice, which is worth doing deliberately for most of a cast — their clips need no regeneration at all. Note the id: `wan_v2.2-14b-fp8_s2v` is not currently accepted by the client's video-model allowlist even though workers advertise it; the `_lightx2v` variant is, and routes. ### Score One cue per region, generated long enough to loop, crossfaded on movement and ducked under anything that has its own audio. ## Two things about pacing an agent build - **Canary before batch.** One line, one clip, one mesh — verified natively and approved — before spending on the set. Every expensive mistake in this build was a batch that should have been a canary. - **A queued job is not a failed job.** Newly seeded models can have very few workers, and a job may sit behind other people's work. Adding a client-side timeout and resubmitting only duplicates work that was always going to complete.