Skip to content
Shekhar Kumar

All projects

Work · Shipped

Distributed speech-to-image pipeline

Live narration in, illustrated story panels out, fanned out across a fleet of GPU pods.

Role
Designer & builder
Organisation
Pure Billion Technologies
Period
Aug 2026 – present
Updated

TL;DR

  1. Live narration in, illustrated story panels out: someone tells a story and its characters and scenes are drawn as it unfolds.
  2. Each scene fans out across parallel GPU pods as competing candidates, rendered in about one job's time, with progress streamed live to the browser.
  3. Designed and built end to end: the Python FastAPI + Celery rewrite, the pod router, the live story pipeline and the AWS GPU deployment, with 115 tests.
scene candidates rendered in parallel
3
GPU instances in production
4
tests
115

How each GPU is wired

Illustrative

Redis · brokerCelery workersGPUs (ComfyUI)HTTP + WSHTTP + WSHTTP + WSHTTP + WSClientbrowser · appFastAPIproducer · routerPostgreSQLJob rowst2i0 in flighti2i.pod11 in flighti2i.pod21 in flighti2i.pod31 in flightpub/subprogress eventsworker · t2iCelery · concurrency 1worker · i2i-pod1Celery · concurrency 1worker · i2i-pod2Celery · concurrency 1worker · i2i-pod3Celery · concurrency 1GPU · t2iComfyUI · Z-Image TurboGPU · i2i 1ComfyUI · Flux.2 KleinGPU · i2i 2ComfyUI · Flux.2 KleinGPU · i2i 3ComfyUI · Flux.2 KleinMinIOPNG results

step 6 of 8

Three GPUs render in parallel

Progress comes back over the ComfyUI /ws websocket. The worker republishes it on Redis pub/sub, and the API streams it to the client over SSE.

event: progress   data: {"jobId": "j2", "pct": 55}
  • request
  • progress
  • image
Watch a request travel the real wiring: the FastAPI producer puts tasks on Redis queues, each Celery worker drains exactly one queue and drives exactly one ComfyUI GPU over HTTP and WebSocket, results land in MinIO and progress streams back to the client. Switch to a portrait request, step through the story pipeline, or run the render-queue simulation. Illustrative, not production data.

The problem

The product turns a spoken story into illustrations in real time. Audio streams in (Hindi is translated to English on the fly), a language model decides which characters exist and what each scene shows, and image models draw character portraits and multi-character scene panels that stay consistent from panel to panel.

The render tier is the hard part. ComfyUI workflows are heavy, long-running GPU jobs, nothing like a millisecond API call. The system needed one job per GPU at a time, several candidates per scene so the best one can be kept, progress people can actually watch, and a GPU fleet that can be switched on and off to control cost.

My role · Designed and built it: the architecture, the migration from a NestJS/BullMQ stack to Python FastAPI + Celery, the queue router and workers, the live story pipeline, and the AWS deployment and operations scripts.

How it works

  1. Stream the narration

    The browser captures microphone audio with an AudioWorklet (16 kHz PCM) and streams it over a WebSocket to a FastAPI session; Sarvam's streaming API turns the speech, including Hindi, into English text.

  2. An LLM directs each scene

    An utterance buffer flushes complete thoughts to a scene director (OpenAI gpt-4o-mini or a self-hosted Qwen3 on vLLM) that registers new characters and decides when a scene is ready to draw.

  3. The producer fans out

    FastAPI writes job rows to PostgreSQL and dispatches Celery tasks: character portraits go to a single text-to-image queue, and each scene panel fans out as one candidate per image-to-image pod, each with its own seed and the characters' reference images.

  4. An in-flight-aware router

    A Redis-backed router counts in-flight jobs per pod and orders pods least-busy first, rotating ties, so back-to-back requests land on different GPUs and adding a pod adds a candidate automatically.

  5. One job per GPU, never lost

    Each Celery worker drives exactly one ComfyUI pod with concurrency 1, prefetch 1 and acks_late, so a crashed worker's job is redelivered rather than dropped. Reference images are deduplicated per pod by content hash, and results land in MinIO.

  6. Progress you can watch

    Workers publish progress on Redis pub/sub; the API streams it to the browser over SSE and WebSocket events, multiplexing every candidate in a batch into one stream.

  7. A GPU fleet on AWS

    A central EC2 instance runs the API and workers behind an ALB; four GPU instances on private subnets run only ComfyUI from a pinned AMI. Scripts start the fleet and warm every model, and on shutdown drain Celery before stopping the GPUs.

Key decisions

  • One queue per pod, behind an in-flight-aware router

    instead of: One shared queue every worker pulls from

    A scene's candidates must land on different GPUs to render in parallel; per-pod queues make placement explicit, and the router keeps load even.

  • Concurrency 1, prefetch 1, acks_late

    instead of: Several workflows per GPU, acknowledged on receipt

    Heavy workflows compete for GPU memory, so one at a time keeps each job predictable; acknowledging late means a crash redelivers the job instead of losing it.

  • Fan candidates out in parallel

    instead of: Rendering one image at a time

    Three idle pods produce three options in about one job's wall-clock time, at three times the GPU cost, and the best one is kept.

  • Push progress over Redis pub/sub and SSE

    instead of: Clients polling job status

    People watch panels fill in live, and one batch stream carries every candidate.

  • Rewrite in FastAPI + Celery, keeping the schema

    instead of: Extending the NestJS/BullMQ stack

    The existing PostgreSQL schema and storage layout were preserved, and the new stack used its own Redis database, so the cutover could run alongside the old one.

  • GPU hosts run only ComfyUI

    instead of: Celery workers on every GPU machine (the earlier topology)

    GPU machines stay stateless, so the whole fleet can be started, warmed, drained and stopped by script to control cost.

Outcomes

  • A live narration-to-illustration pipeline: speech in, character portraits and scene panels out.
  • Scene candidates rendered in parallel across GPU pods in about one job's time.
  • Production on AWS: four 96 GB RTX PRO 6000 GPU instances behind a central API and worker host, started, warmed and drained by script.
  • 115 tests across the producer, the workers and the image-generation package.

Stack

  • Python
  • FastAPI
  • Celery
  • Redis
  • PostgreSQL
  • MinIO
  • ComfyUI
  • WebSockets
  • AWS EC2 GPUs