Skip to main content
Version: 0.12

Image Generation: Text-to-Image

Atelico ships several on-device diffusion models for turning a text prompt into a PNG image — no external API, no network round-trip once the weights are cached.

model valueFamilyDefault stepsDefault sizeNotes
pixart-sigmaPixArt-Sigma20512×512Multi-step DPM-Solver; the steps slider is meaningful
pixart-alphaPixArt-Alpha1512×512Distilled one-step (DMD) generation — fastest
sana-sprintSana21024×1024SCM 2-step distilled
sana-0.6bSana 0.6B201024×1024Flow matching
sana-1.6bSana 1.6B201024×1024Flow matching, higher quality
animaAnima DiT20512×512Supports the distilled rCM few-step sampler

Prefix with the backend as usual: in-memory::pixart-sigma. Unknown ids return a clear error listing the supported models.

The flow is the same for every model: a text prompt goes in, a base64-encoded PNG comes out. Generation takes seconds, so every binding also exposes a non-blocking async variant (*_async) and a step-by-step streaming variant.

Request fields

The request deserializes into an ImageGenerationRequest. The OpenAI-compatible top-level fields:

FieldTypeDefaultDescription
modelstringrequiredModel id, e.g. in-memory::pixart-sigma
promptstringrequiredText description of the image
nu81Number of images to generate
sizestringmodel-specificDimensions as "WxH" (e.g. "512x512", "1024x1024")
seedu64randomRandom seed for reproducible generation
negative_promptstringnoneTerms to steer the model away from
response_formatstringb64_jsonb64_json or url
output_formatstringpngpng, jpeg, or webp
streamboolfalseIf true, emit intermediate denoising steps

Inference-pipeline knobs live under a nested pipeline_config object:

pipeline_config fieldTypeDescription
num_inference_stepsu32Diffusion steps (higher = better quality, slower)
guidance_scalef32Classifier-free guidance (higher = more prompt adherence)
vae_modestring"full" (high quality) or "tiny" (fast, softer)
adapter_weightf32LoRA adapter strength, 0.01.0 (if an adapter is loaded)
use_chiboolSana-only Complex Human Instruction prompt enhancement (default true)
sampling_modestringAnima-only: "base" (default) or "rcm" for the distilled few-step sampler
nfeu32Anima rCM function evaluations — one of {1, 2, 4}
note

LoRA suffixes work here too: in-memory::pixart-sigma::chibi-style loads the pixart-sigma model with the chibi-style adapter. See LoRA Adapters.

Response

The response is an ImageGenerationResponse. Each entry in data carries the generated image as a base64-encoded PNG in b64_json:

{
"created": 1234567890,
"data": [
{"b64_json": "iVBORw0KGgo..."}
],
"output_format": "png",
"size": "512x512"
}

Streaming progress

Pass "stream": true to receive one intermediate ImageGenerationResponse per denoising step — useful for showing a live preview that sharpens as the image resolves. The final event carries the completed image. In the native SDKs this is exposed through the same streaming primitive used elsewhere (Rust StreamHandle, FFI poll loop, etc.).

SDK usage (in-process)

Rust SDK

use atelico_sdk::{Engine, ImageGenerationRequest};

let engine = Engine::new()?;

let request: ImageGenerationRequest = serde_json::from_value(serde_json::json!({
"model": "in-memory::pixart-sigma",
"prompt": "a cat sitting on a roof at sunset, watercolor",
"size": "512x512",
"seed": 42,
"pipeline_config": { "num_inference_steps": 20 }
}))?;

// Blocking — returns once the image is fully generated.
let response = engine.images().generate_sync(request)?;
let b64 = response.data[0].b64_json.as_deref().unwrap();
let png_bytes = base64::engine::general_purpose::STANDARD.decode(b64)?;
std::fs::write("cat.png", &png_bytes)?;

Generation is seconds-long, so prefer the non-blocking variant in a game loop: engine.images().generate_async(request) returns a [StreamHandle] you can drive via poll() (frame integration), next() (async), or next_blocking() (sync). generate_stream(request).await yields the per-step previews.

Python

import base64, json
from atelico import Engine

engine = Engine()

resp = json.loads(engine.image_generate(json.dumps({
"model": "in-memory::pixart-sigma",
"prompt": "a cat sitting on a roof at sunset, watercolor",
"size": "512x512",
"seed": 42,
"pipeline_config": {"num_inference_steps": 20},
})))
png = base64.b64decode(resp["data"][0]["b64_json"])
open("cat.png", "wb").write(png)

# Non-blocking — returns a JsonResultStream you poll for the final response.
handle = engine.image_generate_async(json.dumps({
"model": "in-memory::pixart-alpha",
"prompt": "a fantasy castle on a cliff",
}))

C / FFI

// Blocking image generation
const char* request =
"{\"model\":\"in-memory::pixart-sigma\","
"\"prompt\":\"a cat sitting on a roof at sunset\","
"\"size\":\"512x512\",\"seed\":42,"
"\"pipeline_config\":{\"num_inference_steps\":20}}";
const char* response_json = NULL;
int rc = atelico_image_generate(engine, request, &response_json);
// response_json: {"created":..,"data":[{"b64_json":"iVBORw0KGgo..."}],
// "output_format":"png","size":"512x512"}
// base64-decode data[0].b64_json to recover the PNG bytes.

For non-blocking generation, atelico_image_generate_async takes the same request JSON and returns a stream handle; drive it with the shared atelico_stream_poll / atelico_stream_destroy loop (see the C FFI getting started guide). The output pointer is owned by the FFI layer's thread-local return buffer — copy it before the next API call on the same thread.

Demo

demos/image-gen is a desktop app (also iOS-buildable) that exercises the full path: pick a model (pixart-sigma, pixart-alpha, or anima), type a prompt, adjust steps/seed, and view the result. Select the model via the ATELICO_DEMO_MODEL env var (anima | pixart-sigma | pixart-alpha). It is the recommended starting point before wiring image generation into a game engine.

See also