# Imagination Studio by keys+Wasserman, powered by TensorFold (Mac + DGX Spark) *Formerly keys+Wasserman Slate AI Studio, built with integrated Fish Audio Drama 3.* **Keys:** [X @u1tra_instinct](https://x.com/u1tra_instinct) · [GitHub](https://github.com/drowzeys) **Sam Wasserman / Wasserman Productions:** [wassermanproductions.com](https://wassermanproductions.com) · [X @SamJWasserman](https://x.com/SamJWasserman) · [GitHub](https://github.com/wassermanproductions) **An AI film studio on your own machine: plan a film the way a crew does in the Wasserman Slate AI Studio, and render it here. Pictures from Qwen-Image-2.1-Turbo, FLUX3-K (Black Forest Labs' FLUX 3 Image Klein) or MOSS-Image, MiniMax H3 / FastH3 video with sound and Fish Audio Drama 3 voices, on TensorFold: Apple-silicon Macs (Metal) and the NVIDIA DGX Spark (CUDA).** ![Imagination Studio by keys+Wasserman, powered by TensorFold: a local creative all-in-one studio for NVIDIA DGX Spark or Apple silicon](docs/screenshots/imagination-studio.webp) ## Minimum hardware requirements (read this first) One machine is a complete Studio: a DGX Spark, an RTX Pro 6000 workstation **or** a Mac. You do not need more than one, and none needs another. The installers check memory and disk before they download anything and stop with the reason when the machine is too small. ### NVIDIA DGX Spark GB10, 128 GB unified memory, DGX OS with CUDA 13 and Docker. Every feature runs. | Install | Disk free | Command | |---|---|---| | Core: pictures and FastH3 video with sound, motion graphics, Finish, phone notifications | 125 GB | `bash oneshot-setup.sh` | | **Everything**, with training a LoRA and every big model | **about 400 GB** | `bash oneshot-setup.sh --all` | ### NVIDIA RTX Pro 6000 Blackwell 96 GB card, x86_64 Linux workstation (Ubuntu 22.04 or 24.04, NVIDIA driver 580 or newer, the CUDA 13 toolkit, 64 GB of RAM or more, 128 GB recommended). The core install runs on the card: pictures and FastH3 video with sound. `oneshot-setup-rtx-6000.sh` names the card and runs `oneshot-setup-cuda.sh`, the same install `oneshot-setup-spark.sh` runs. That install builds the engines for the card (sm_120) and the Studio reads the card's own memory; the Bolt brain runs through llama.cpp there. [docs/rtx-pro-6000.md](docs/rtx-pro-6000.md). A 32 GB RTX 5090 can run video too, as an opt-in that keeps the weights in RAM: [docs/rtx-5090.md](docs/rtx-5090.md). `oneshot-setup.sh` on Linux reads the first GPU. Use the command below. Set the four variables for the disk and the card. `/data/studio` is an example path. No flag is the core (125 GB). Flags are listed under [Install flags](#install-flags). | Variable | Default | Purpose | |---|---|---| | `PREFIX` | `~/.local/opt/tensorfold-studio` | Environment, ComfyUI, and the engines | | `SPARK_MODELS` | `~/spark-studio-models` | Weights. This is the part that fills a disk | | `STUDIO_HOME` | `~/ImaginationStudio` | Projects, renders, uploads, and settings | | `STUDIO_GPU` | unset | nvidia-smi index of the card. Required when more than one GPU is not driving a display. Indexes differ by machine | ```bash PREFIX=/data/studio SPARK_MODELS=/data/studio/models \ STUDIO_HOME=/data/studio/ImaginationStudio STUDIO_GPU=1 \ bash oneshot-setup-rtx-6000.sh ``` The install's `DONE` line prints `STUDIO_GPU` and `scripts/app.sh` only. Pass the same four values again or the app looks under `$HOME` and does not find this install. It serves http://127.0.0.1:7870. ```bash PREFIX=/data/studio SPARK_MODELS=/data/studio/models \ STUDIO_HOME=/data/studio/ImaginationStudio STUDIO_GPU=1 \ bash scripts/app.sh ``` Ctrl-C in that terminal stops the app and the ComfyUI for this install, and the card is freed. The same quit works on a DGX Spark. ComfyUI stays loaded between clips while the app is running. To free the card and leave the app up, run this from the repo. `PREFIX` is this install. `STUDIO_GPU` is required when more than one GPU is not driving a display. ```bash PREFIX=/data/studio STUDIO_GPU=1 bash spark/comfy.sh stop ``` ### Other NVIDIA GPUs Untested. The CUDA engines are built and measured for the GB10. ### Apple silicon macOS 26 or later, for Metal 4. What runs depends on the memory. Every figure was measured on a Mac Studio M5 Ultra with 256 GB; smaller Macs and M1 to M4 follow from those measurements and are untested. | Memory | M5 family | M1 to M4 | Disk free for `--all` | |---|---|---|---| | 48 GB | Pictures only | Pictures only | 40 GB | | 64 GB | Pictures, FastH3 video, voice, music, sound, MOSS-Image, FLUX3-K, the Bolt brain | The same, video on the slower MLX engine, no Bolt | about 400 GB (440 on M1 to M4) | | 128 GB | Everything above, plus MiniMax H3 Turbo and reference pictures to video (Ref2VA) | The same, slowly | about 530 GB | | 192 GB or more | **Everything**, including Upscale and Remove a person (peaks of 125 and 135 GB) | The same, slowly | about 535 GB | `bash oneshot-setup.sh --all --app` installs everything your Mac's memory runs and the double-clickable app. It says which features it left out and why (training a LoRA always: it runs on a DGX Spark). Before that, `brew install python@3.11 uv ffmpeg`. ### Install The one-click install, `bash oneshot-setup.sh` with no flag (on a Mac, a DGX Spark or an RTX Pro 6000; `--app` adds the double-clickable Mac app), contains: pictures (Qwen-Image-2.1-Turbo) and FastH3 video with sound, the essential LoRA bundle, motion graphics (HyperFrames, about 400 MB), Finish (Tesseract and its converter, about 150 MB; the fonts only on a DGX Spark, which finishes the cut on a Mac), phone notifications (`cryptography`, `pywebpush`) and the speech-to-text for captions and the speech check (`faster-whisper`). `--all` adds every big model the machine runs (Ref2VA, FLUX3-K, MOSS-Image, MiniMax Music 3, Fish Audio Drama 3, sound effects, Nemotron 3.5 Bolt, Motion Previs) and, on a DGX Spark, training a LoRA. `--verify` reports each of these parts, and `--upgrade` moves the parts already installed to this version. Run in a terminal with no flag, the install first shows a checklist of every part the machine runs, each with its download size, grouped into what the plain install includes and the larger optional models, with the total beside the free disk space. Everything starts ticked: type a row's number (or `3 5`, or `4-6`) to untick or tick it, `n` to untick every optional model, `d` to tick everything again, and press Enter to install what is ticked; the install prints the same choice as flags so it can be run again without the list. `--yes` (or any flag) skips the list and installs as before, with the plain install's parts; so does a run that is not in a terminal. FLUX3-K and sound effects are gated on Hugging Face: before anything is downloaded the install checks that your signed-in account (`hf auth login`) has accepted each one's licence. One that has not is left out with the page to accept it, everything else installs, and the end of the run prints the command that adds it afterwards (`--verify` reports it as waiting for the licence). ### Install flags For `oneshot-setup-rtx-6000.sh` and `oneshot-setup-spark.sh`; `oneshot-setup.sh` takes the same names on a Mac (`--train` is said and left out there). A DGX Spark does not set `STUDIO_GPU`. No flag is the core: pictures and FastH3 video with sound (125 GB), motion graphics, Finish and phone notifications. | Flag | What it installs | |---|---| | `--all` | Every feature below, about 400 GB (on a Mac, every feature its memory runs, without `--train`) | | `--image-only` | Qwen-Image-2.1 only (26 GB) | | `--ref2va` | Reference pictures to video (39 GB more) | | `--swap` | The Library's Character Swap: MiniMax H3 Ref2VA in int8, two LoRAs and SAM 3.1 (23 GB more; 2.1 GB where the int8 Ref2VA is already there). DGX Spark and CUDA only | | `--h3-base` | MiniMax H3's own transformer, for keyframe LoRAs and the Library actions (40 GB more) | | `--image-edit` | Qwen-Image-2.1's 40-step model (14 GB more) | | `--flux3` | FLUX3-K pictures (41 GB more, gated) | | `--moss` | MOSS-Image (30 GB more) | | `--music` | MiniMax Music 3 (22 GB more) | | `--yue2` | YuE2 songs, the Music tab's default engine on Linux with an NVIDIA GPU (about 14 GB more; not on a Mac) | | `--bolt` | Nemotron 3.5 Bolt, the studio's brain (about 25 GB more) | | `--sound` | Sound effects and foley (11 GB more, gated) | | `--motion` | Motion Previs analysis (0.5 GB) | | `--voice` | Fish Audio Drama 3 (about 41 GB more) | | `--train` | Train your own LoRA: musubi-tuner, MiniMax H3's video encoder and a training adapter (an 8 GB environment, its two files, about 6 GB, and, when `--h3-base` has not installed it, that 40 GB transformer; the CUDA build, a DGX Spark or a GPU, not on a Mac) | | `--graphics` | Motion graphics: HyperFrames, GSAP, Node 22 when the machine's is older, and a browser (about 400 MB). In the default install; `--no-graphics` leaves it out | | `--finish` | Finish: Tesseract 0.4.0 and Tesseract Converter 0.3.0, two fonts (about 150 MB; the fonts only on Linux ARM such as a DGX Spark). In the default install; `--no-finish` leaves it out. Tesseract is free under Mirage's own terms, not open source; the converter is MIT | | `--no-render` | Skip the test clip | | `--no-loras` | Skip the essential LoRA bundle (about 4 GB) | | `--no-workflows` | Skip the Library action packs | | `--yes` | No checklist: the plain install (or what the other flags say), as in a script | | `--verify` | Check an existing install and download nothing | | `--upgrade` | Move an existing install to this version and fetch no weights | Finish (the final cut and mix, to Resolve, Premiere or After Effects) is also its own installer, `bash scripts/setup-finish.sh` (`--verify` only reports). Details: [Finish](#finish-the-final-cut-and-mix-to-resolve-premiere-or-after-effects). ### Accounts FLUX3-K and Stable Audio Open (sound effects) are gated on Hugging Face. Accept their licences with your account and run `hf auth login` before installing them; the installer says so when access is missing. Full recipes, every option and the measured memory of each job: [Install, and the one-click app](#install-and-the-one-click-app) (Mac), [DGX Spark (CUDA) build](#dgx-spark-cuda-build). *Named at the request of the TensorFold project: this is an app built on TensorFold, not a TensorFold product.* *Name, credits, colours and voice for anything public: the [brand guide](docs/BRAND.md).* *Credit first: **Keys (drowzeys)** built this studio and its engines; **Sam Wasserman and Wasserman Productions** helped design and build the studio in collaboration with Keys, made Wasserman's Filmmaker Suite, and shaped the production side (Slate, the Sharp Spec, Motion Previs). See [Big credit: Sam Wasserman and Wasserman Productions](#big-credit-sam-wasserman-and-wasserman-productions-wassermanproductionscom), below the release notes, and [CREDITS.md](CREDITS.md).* **4.2.1** · Apple-silicon Macs (measured on a Mac Studio M5 Ultra 256 GB), the NVIDIA DGX Spark, and the NVIDIA RTX Pro 6000 (96 GB) · [TensorFold](https://github.com/ashhart/TensorFold) **0.6.5** + two families, and TensorFold **1.0**'s native Zig runtime for FastH3 on Metal and on CUDA, TensorFold **1.1.8** for MiniMax Music 3 and both Bolt engines (**1.1.5** under YuE2): **Qwen 2.1 Turbo**, **Qwen 2.1 Full**, **FLUX3-K Fast** and **FLUX3-K Full** (text to image, and Edit a picture) · **MOSS-Image** (text to image, DGX Spark) · **MiniMax H3** (video with sound) · **Fish Audio Drama 3** (Voice) · **YuE2** and **MiniMax-Music3** (Music: songs with sung vocals) · int8 kernels on the M5 tensor units and on the GB10's tensor cores · a 2x video decoder for 2K finals on the Mac ## New in 4.2.1 (2026-10-29) No build machine's folders or user name ship in a prebuilt engine any more. Three CUDA binaries in the carrier, built before 3.3.0 and copied unchanged since, held their build folders in their debug sections: the H3 engine (`libtf_h3.so`) and Drama 3's `drama3-generate` and `libtensorfold_drama3.so`. So did the symbol table of the Mac's Bolt binary in the 4.2.0 release archive. Only those parts change: the debug sections and symbol tables are dropped. Every loaded segment (code, data, CUDA kernels) is byte for byte the same; only the file headers that point at the removed sections differ. To update, `git pull` and restart the app. An installed engine has the same commit and is kept; a new install takes the stripped one. - **Same output, checked on a DGX Spark, in the order old, new, new, old:** - H3: a 5 s clip at 864x480 (FastH3, 8 steps, seed 7). The video stream, the decoded frames and the audio are identical in all four runs; 117.6–118.4 s new against 118.1–124.6 s old. - Drama 3: three lines in the default voice at seed 7. The WAV files are byte-identical in all four runs; median first audio 256 ms new against 249 ms old, within run-to-run noise. - **The same on the Mac:** Bolt on Metal gives 7 of 7 identical greedy replies, with first tokens and tokens a second within noise. - **Where they come from:** - On a DGX Spark, H3 and Drama 3 now come from the carrier `ghcr.io/drowzeys/keys-tensorfold-studio:4.2.1`. - The Mac's Bolt comes from this release's `bolt-metal-772da34b-macos-arm64.tar.gz` (a new SHA-256, pinned in `scripts/setup-bolt.sh`). - Everything else stays on 4.2.0's parts. - **It stays fixed:** - `scripts/build-carrier.sh` drops the debug sections of every CUDA engine it copies. - It refuses to build a carrier whose payload still names the building machine's home folder or user. - **Scan:** no file in the 4.2.1 carrier, nor in either Bolt archive, contains a home folder or a user name. ## New in 4.2.0 (2026-10-29) The engines built on TensorFold move from **1.1.5** to **1.1.8**, the latest release, with the same output: Nemotron 3.5 Bolt on a DGX Spark and on a Mac, and MiniMax Music 3 on both. New prebuilt parts come with it: the carrier `ghcr.io/drowzeys/keys-tensorfold-studio:4.2.0` and the release's Bolt archives for CUDA and Metal. To update, run `git pull`, then `bash oneshot-setup-spark.sh --upgrade` (or `oneshot-setup-rtx-6000.sh --upgrade`) on Linux or `bash oneshot-setup.sh --upgrade` on a Mac, and restart the app. The engine each one replaces stays beside it (`bolt.previous`). - **Bolt on a DGX Spark** (drowzeys/TensorFold `bolt-lilicorr-118`): our LiLiCorr drafter patch on TensorFold 1.1.8. The only conflict was the CUDA family list, where 1.1.6 added Spark-X2.6. - Replies are identical, 7 of 7 greedy prompts in three alternating runs. - Median first token 0.062–0.084 s against 0.064–0.102 s on 1.1.5; 74–82 tokens a second against 73–76. - Memory is the same: 0.27 GiB a stream at 16k tokens. - 1.1.8 also brings `--spill-gib` for Nemotron on CUDA and admission from the shard headers. - **Bolt on a Mac** (`bolt-metal-118`): identical replies on 7 of 7 in two passes. The first token came in 0.119 s against 0.126 s, at 134 tokens a second on both, with the same memory. The patch that fixed one Drama 3 seed for every request is dropped: TensorFold 1.1.6 takes a seed with each request instead. - **MiniMax Music 3** (`music3-cuda-118`, `music3-metal-118`): the song's bytes are identical on 1.1.5 and 1.1.8 for the same seed. - DGX Spark: 57–58 s for a 20 s int8 song on both, with the same 16.6 GB peak. - Mac: 23.2 s on both. - **Kept on their pins:** - **YuE2's decode engine stays on 1.1.5** (`music3-cuda-115`). The engine's GPU test passes on 1.1.8, but a whole song was not timed against it. - **Drama 3 on the DGX Spark stays on its engine** (plotarmordev's NVFP4 lineage, 96 commits on 1.0.7), which is not rebased. - **The H3 engines are unchanged.** The carrier holds the same H3 and Drama 3 builds as 3.3.0. - **Zig is checked before it is unpacked, in every build:** - The CUDA builds of Bolt, H3, Music 3 and Drama 3 fetched Zig without a check. They now share `spark/zig.sh`, which checks the archive against the SHA-256 ziglang.org publishes and refuses a mismatch. - The Mac's Music 3 build had an empty checksum and unpacked Zig unverified; it now has the published sum. - **No machine paths in the engines.** The CUDA Bolt and Music 3 builds are made under neutral paths: `strings` finds no home folder in any binary, and the archives are packed owner 0/0. ## New in 4.1.2 (2026-10-28) Fixes for machines with two or more NVIDIA cards, and for a Spark without Qwen. Update the clone, run `--upgrade` on Linux and restart the app; a Mac only needs the pull and a restart. - **Two or more NVIDIA cards and no `STUDIO_GPU`:** every installer entry (`oneshot-setup.sh`, `oneshot-setup-cuda.sh`, the Spark and RTX scripts) now chooses the card the way the app's start script does. A single compute card is used even at index 0 beside a display card, so a workstation with a small display card first no longer installs against it (or refuses video because "this one has 8 GB"). With two compute cards the installer stops and asks for `STUDIO_GPU`. The memory guard reads the same card when the server is started on its own. - **A script that says why it stopped is reported in its own words**, without " (status 1)" after it; the status is kept only when the script printed nothing ("the script stopped (status N)"). Swap refusals read as written. - **Qwen is optional where FLUX3-K is installed:** `--verify` and `--upgrade` say "Qwen-Image-2.1-Turbo is not installed (optional: pictures use FLUX3-K …)" instead of stopping at "✗ missing", and no longer print a ✓ for files they just called missing. studio-check marks the Qwen edit and angle checks as not applicable on such a machine. ## New in 4.1.1 (2026-10-28) Fixes to 3.4.0 and 4.1.0 on all four machines: the DGX Spark, the RTX Pro 6000, a 32 GB RTX 5090 and the Mac. Update the clone and restart the app; `--swap` installs fetch the int8 Ref2VA they run on (below). - **Character Swap checks memory before it is queued**, like a clip, a song or a sound, from peaks measured on a DGX Spark and on a Spark capped to 96 GB and to 32 GB: 55.4 / 60.6 GB of a Spark at Fast / High, 49.4 / 52.7 GB on a 96 GB card, and with `STUDIO_ROOM_MB` on a 32 GB card the machine gave 37.8 / 39.5 GB (docs/rtx-pro-6000.md, docs/rtx-5090.md). An RTX 5090 without `STUDIO_ROOM_MB` is refused with the card's name and what to set; with it, ComfyUI keeps the models in RAM and the swap runs. A Mac is told it has no swap before anything about its clip. - **Nobody in the part is refused in words.** SAM 3.1 must be at least 75% sure of a person (people scored 0.93 to 0.99 on synthetic frames, empty rooms, a coat on a rack, gradients and a Mandelbrot 0.31 to 0.67). A flat colour no longer fails with *index 0 is out of bounds*. - **A cancelled or failed swap leaves nothing behind**: no mask preview, no anchor. - **`--swap` fetches MiniMax H3 Ref2VA in int8 (21 GB)**, the weights it runs on, instead of requiring the 40 GB bf16 file; the Library's actions run on either. The disk check counts it on each machine. - **The Director** says why the QA gate failed a take and keeps the gate's report on the take; a run with nothing to cut still ends with its counts; the cast's and places' stills use a picture model this machine has (FLUX3-K Fast on a Spark without Qwen), only for who and where the shots show; a written age is not followed by "years old", and a brain's None or N/A never reaches a prompt. - **YuE2**: its refusals (when it loads and when it is queued) give the figures to a tenth of a gigabyte, and on a card name the card and `STUDIO_ROOM_MB`; a song cancelled while Whisper listens leaves no take behind, a failed song leaves no score folder and its server gives back PyTorch's cache, the Music tab says why YuE2 is not offered where it is not (a Mac), and an RTX machine is told its own installer. Its engine build checks Zig's SHA-256 and tests on the GPU only when it is idle. - **The Library** lists failed takes however a project file is shaped (a hand-edited one gave a 500). - **Repo**: no machine's paths or job owners in the receipts; tiktoken, the Qwen team's `qwen.tiktoken` and Openjutsu credited in CREDITS.md and Settings; tests that read the machine they ran on stand in for it, and none of them starts a real render or ComfyUI, or mistakes a studio already running beside them for their own. ## New in 4.1.0 (2026-10-27) The Director tells you what really happened, and shows you what did not work. Nothing to install: update the clone and restart the app (`git pull`, then quit and reopen the Mac app, or `bash scripts/app.sh`). - **An honest run report.** The run's last line counts what passed the check, what was not checked, and by name what the queue refused, what failed to render and what failed its check, then says what the cut is missing (*5 of 11 shots passed the check, 3 refused by the queue: Gull Strike, Pier Fall, Night Ferry, ..., cut without 6 shots*). A shot the memory guard refused is never counted as rendered, and the refusal says what the Studio had loaded beside the queue at the time. When the run is done the panel lists each shot left out of the cut with its reason. - **A failed take stays where you can see it, out of the cut.** A take that fails its check is rated *no good* and kept among the shot's takes and in the **Library**, marked *failed check* (type "failed" in the Library's search to list them). So are the takes the QA gate and the shot check keep when they render a clip again. The Director cuts in only takes that passed, or not checked, unless you circle a failed one in Slate. With no retakes the choice reads *No retake* (it said *Keep it*). - **Shots keep their people.** The shot check sends the brain's shots back when a character is named in the prompt and the shot's cast is empty, or when someone speaks inside the prompt and the shot has no lines: their pictures and voices would never reach the clip. Slate's preflight says so too when a character is named in a prompt but not cast. - **A new angle opens on the cast.** A new size or angle is a cut, opening on the cast's stills (same pictures, same order, no first frame) where Ref2VA is installed; only a beat carried on in the same size and angle opens on the shot before. "The shots of a scene follow each other without a cut" is gone from what the brain is asked. - **Costumes in the reference stills.** When a character's clothing names a hat, a coat or a uniform, the reference portrait is from the knees up with all of it in frame, instead of a head-and-shoulders with "no props". - **A brain reply with a stray think tag still reads.** When a reply carries ``, the answer after it is read; when the first JSON in a reply is broken, the longest whole one is. - **One render in the queue for each machine.** The Director keeps one clip in the queue for each machine that renders (this one and its Sparks, at most three), so on one Spark a retake no longer waits behind the next shots. A render the machine's workload limits stopped (heat, memory pressure) is not a take: it is queued once more, at the same seed, after the queue's own cool-down gate, and left out after a second stop. The limits themselves are unchanged. - **Setup.** `scripts/setup-voice.sh` stops at once, before the engine, when the folder for Fish's checkpoint is not yours to write (a root-owned `~/models`), and says how to fix it. An app that is already running picks up the Bolt engine within a minute of `oneshot-setup-spark.sh --bolt`, with no restart (before, it stayed *external* until one). ## New in 3.4.0 (2026-10-27) Two new things to make, both on this machine with no account and no cloud: songs by **YuE2**, now the Music tab's default on Linux with an NVIDIA GPU, and **Character Swap**, a Library action that puts the character in one picture in place of the person in a part of a clip. Add them to a DGX Spark (both are ticked in the installer's checklist and part of `--all`), then restart the app; after that, `--upgrade` keeps them up to date: ```bash git pull && bash oneshot-setup-spark.sh --yue2 --swap --no-render ``` A Mac has nothing new to install: neither runs on a Mac, and its Music tab stays on MiniMax Music 3. - **YuE2 songs.** m-a-p's YuE2-3B and its YuE2-Vae decoder write the song's score first (melody and chords), then the song: sung vocals and accompaniment, 48 kHz stereo, up to five minutes. It runs as a resident server in an environment of its own (`$PREFIX/yue2`); on a DGX Spark the song's tokens run on TensorFold's CUDA engine (int8). Measured on one DGX Spark: a 60 s song in about **38 s**, a 2-minute song in about **64 s**, the model loaded in 6 to 9 s, and Whisper heard **94 to 97 %** of the words sung. MiniMax Music 3 stays one click away: the **engine picker** above the Music tab's form (YuE2 · MiniMax Music 3) chooses for each song, and Settings → Presets → *Song engine* sets the default (*Best here*: YuE2 where it is installed, MiniMax Music 3 on a Mac). Each take keeps its score (*Download score*). `--yue2` is about 14 GB. [YuE2 songs](#yue2-songs). - **Character Swap.** *Swap the character* under a clip in the Library: choose a picture of the character and a part of the clip (5 to 15 seconds), and the person in it is drawn again as that character, with the clip's own scene, performance and sound kept. **Fast** (480p, the default) or **High** (768p); the picture model first puts the character into the part's first frame and the swap starts from it (*Put the character in the first frame first*). It runs on the studio's own MiniMax H3 Ref2VA (int8), with LightX2V's 8-step Turbo LoRA, Akatz Labs' Character Swap LoRA v1.5 and SAM 3.1 to find and follow the person. Measured on a DGX Spark with the GPU to itself: a 5 s part in about **6 minutes** at Fast and about **12 minutes** at High. A part with a hard cut, or with nobody in its first frame, is refused before anything is drawn. DGX Spark and CUDA only. `--swap` is 23 GB: the int8 Ref2VA it runs on (21 GB, without `--ref2va`'s 40 GB bf16 file) and 2.1 GB beside it. [Character Swap](#character-swap). Licences, also in Settings → About and beside each choice in the app: - **YuE2:** the code is Apache 2.0; the weights are CC BY-NC 4.0 with the YuE2 authors' additional permission for creators: personal users, content creators and musicians may publish, sell and license the songs they make, free and with no royalties; a company needs a commercial licence from the authors. Do not redistribute the weights. - **Character Swap:** MiniMax H3 and Akatz Labs' Character Swap LoRA are under the MiniMax H3 Community License, which does not cover use in the EU, the UK, South Korea or the US; LightX2V's Turbo LoRA is Apache 2.0; SAM 3.1 is under Meta's SAM License. The idea and its settings come from Comfy Org's Openjutsu and Akatz Labs' workflow (MIT). The TUTORIAL and the app's **Learn** tab have a lesson for each: *A song with YuE2 or MiniMax-Music3* and *Character Swap*. ## New in 3.3.3 (2026-10-26) The DGX Spark's brain engine, **NVIDIA Nemotron 3.5 Bolt**, moves from Mia's 1.0.12 recipe (6f61d772 on TensorFold 46a08c18) to the same recipe at 89e1c8a (with ChriZzn's speed work) rebased onto **TensorFold 1.1.5**, drowzeys/TensorFold branch `bolt-lilicorr-115`. NVIDIA's LiLiCorr drafter still runs inside it, and structured output (`libtfgrammar`), pictures and video (`libtfvideo`) are in the build. Two changes over the recipe, neither touching the arithmetic: the first token goes out before the drafter's first proposal, and a chat turn's last prompt-cache mark is kept at the prompt's end, so a turn's new rows run in one pass instead of two. Measured on a DGX Spark (GB10) with the Studio's own settings (4 streams, 65,536 tokens, 2 GiB of prompt cache, pictures on), the same weights, one engine at a time, through a throwaway Studio (`/api/bolt/load`), greedy: | | 3.3.2 engine (1.0.12) | 3.3.3 engine (1.1.5) | |---|---|---| | chat, 120 tokens | 57.0 tok/s, first token 128 ms | **65.8 tok/s**, 77 ms | | 600-token plan | 67.6 tok/s, 156 ms | **76.1 tok/s**, 106 ms | | edit (rewrite a paragraph) | 117.6 tok/s, 209 ms | **137.5 tok/s**, 143 ms | | four plans at once | 140 tok/s | **204 tok/s** | | held while loaded | 28.1 GB | 30.7 GB | Both pass the same checks: a Slate plan through `/api/slate/brain` (JSON, four shots), tool calls (offered, forced and named), `response_format` json_object and json_schema (the grammar holds; greedy, Bolt can run a schema's integer to the token limit on both engines), a picture and a short video, an edit. `spark/build-bolt.sh` now takes the engine from this release's archive (`bolt-cuda-ecdc7892-linux-aarch64.tar.gz`, sha256 pinned), then the carrier, then a build from source (nvcc, Zig 0.17.0, FFmpeg, libjpeg-turbo, xgrammar; about 15 minutes), and keeps the engine it replaces in `$PREFIX/bolt.previous` (move it back to `bolt` to roll back). Update a Spark with `git pull && bash oneshot-setup.sh --upgrade`, then restart the app. The Mac keeps its Metal Bolt engine. ## New in 3.3.2 (2026-10-25) The Mac release assets were repacked without a local username: the archives' file owner is now 0/0, and two old engine binaries (`bolt-metal-b4edaea5`, `zig-engine-4741fd0a`) lost their symbol tables, which held a build folder's path. Their code and data are byte for byte the same. The FastH3 engine `zig-engine-b19753ab-macos-arm64.tar.gz` therefore has a new checksum, which `oneshot-setup.sh` now pins. An install from 3.3.1 or earlier that downloads it fails the old checksum and falls back to the carrier or a source build, so update to 3.3.2 (`git pull`). ## New in 3.3.1 (2026-10-25) 3.3.1 moves the Mac's two TensorFold Metal engines onto **TensorFold 1.1.5**, each checked against the engine it replaces on a Mac Studio M5 Ultra (same weights, one engine at a time, spare ports, a memory guard). Update with `git pull && bash oneshot-setup.sh --upgrade`, then restart the app: on a Mac that has them, `--upgrade` now also rebuilds the Metal music engine at the new commit (a few minutes) and downloads the new Metal Bolt engine (5 MB, checksummed); it fetches no weights. A DGX Spark has nothing new to install. - **Music on a Mac (M5 or later) runs on TensorFold 1.1.5.** The music3 Metal family (`music3-metal-115`, 91877e79) is the same code on the 1.1.5 runtime. A 12 s song, seed 7, two rounds each: **the same song, byte for byte** (identical WAV), made in 13.1 s on both. - **Bolt (the brain) on a Mac runs on TensorFold 1.1.5.** The Metal Bolt engine (`bolt-metal-115`, be300c4d, `libbolt.dylib` behind TensorFold's Bolt lane backend) with the studio's own settings (four streams, 65,536-token window, 2 GiB prompt cache, LiLiCorr drafts), against `nemotron-bolt-metal` b4edaea5 (TensorFold 1.0.12): **the same replies** (7 of 7 greedy answers identical), first token 66 to 72 ms against 68 to 71 ms, 113 tokens/s on both (109 on a 400-token plan), 27.4 GB resident on both, ready in 7 to 8 s. ## New in 3.3.0 (2026-10-25) 3.3.0 moves the studio's engines onto **TensorFold 1.1.5** where that is a safe, measured step, and says why the rest stay where they are. Every engine was put side by side with the one it would replace on the same machine and weights (one engine at a time, spare ports, a memory guard). Update with `git pull && bash oneshot-setup.sh --upgrade`, then restart the app; a DGX Spark takes the new music engine from the carrier `keys-tensorfold-studio:3.3.0` (checksummed). - **Music on a DGX Spark runs on TensorFold 1.1.5.** The music3 family (`music3-cuda-115`, 5505dbfa) is the same code on the 1.1.5 runtime, build and loaders. Measured on one GB10, a 30 s int8 song, seed 7, two rounds each: **the same song, byte for byte** (identical WAV), the same making time (61.0 to 62.5 s against 60.9 to 63.6 s) and the same peak (16.46 GB), and the engine is ready **2.6x sooner** (12.5 to 16.2 s against 32.6 to 69.8 s), so the first song after an idle unload starts sooner. - **Kept, with the measurements** (DGX Spark, same weights, alternating rounds): - **Bolt (the brain) on CUDA** stays on MiaAI-Lab's recipe (TensorFold 1.0.12 base with `libbolt.so` and the LiLiCorr drafter inside it). TensorFold 1.1.5's own Bolt, which drafts with its MTP head (it refuses a separate drafter), decodes 36.5 / 36.9 / 50.2 tokens/s (chat, a 600-token plan, an edit) and 115 with four streams, against the studio engine's **56.1 / 68.8 / 118.9 and 153**. 1.1.5 is first to the first token (75 to 135 ms against 130 to 230 ms), not enough to trade for half the speed. - **Drama 3 (Voice) on CUDA** stays on the studio's `drama3-cuda` engine: 33.9 frames/s, a long line in 14.8 s. TensorFold 1.1.5's Drama serve is 0.77 real-time factor at best (128-row prefill and the silence guard; 15.5 s for the long line, though it streams the first audio in 0.2 s), and in its default setting the long line runs on into non-speech (32.7 s of audio for a 23 s line). 1.1.5 changes Drama only on Metal. - **The native FastH3 engine** (Metal and CUDA, int8) stays on its TensorFold 1.0.4 base: TensorFold 1.1.5's H3 family is a bf16 rewrite of the same files, so the studio's int8 kernels would be a port, not a rebase, for no change in what 1.1.5 offers H3. - **The Mac's MLX engines** (H3, Qwen-Image, the Drama 3 and Music 3 MLX ports) stay on TensorFold 0.6.5: 1.1.5 has no Python or MLX package. ## New in 3.2.0 (2026-10-22) 3.2.0 is the studio's code: its engines are the 3.1.0 builds (the carrier `keys-tensorfold-studio:3.2.0` and the v3.2.0 release hold the same ones, checksummed as before), so an update from 3.1.0 downloads no weights and no engines. Because 3.2.0 adds install parts, the app's own updater asks for the installer first: `git pull && bash oneshot-setup.sh --upgrade` on a Mac or a DGX Spark, then restart the app. `--upgrade` from 3.1 adds motion graphics and Finish (about 550 MB); training a LoRA is added with `--train` on a DGX Spark. Every new part has its place in the sidebar, and **Creative Engines** are in the order a film is made: Video, Image, Sound, Story, Cast & Places, Director, Previs, Motion graphics, Edit and Night shift (⌥1 to ⌥6 follow it). Under **Creative Engines**: **Cast & Places** (the characters' and locations' sheets, with the bible and dialogue choices on top), **Director**, **Motion graphics** and **Night shift**, each with a film picker that starts on the film open in Production, else the newest. **Edit** gains a **Finish** tab, Production (Slate) a **Rough cut** tab beside Deliver, and **Looks** a **Train a LoRA** tab beside its looks. Each is also one ⌘K away. On a phone the bar keeps the main engines; the new rooms are under **More**. Making the film, in the order you would use it: - **The bible and dialogue.** Slate's bible keeps faces, places and voices the same in every shot, and a film's lines can be spoken by the video model or by Drama 3 and matched. [Faces, places and voices](#the-bible-keeps-faces-places-and-voices-consistent), [Dialogue](#dialogue-by-the-video-model-or-by-drama-3). - **The Director.** Give it a logline and it writes, casts, renders, checks and cuts a whole film while you watch. [The Director](#the-director-from-a-logline-to-a-checked-cut). - **The night shift.** Plan a batch before bed and wake up to a reel, a dailies package and a notification on your phone. [The night shift](#the-night-shift). - **A rough cut on the beat.** Cut a film's best takes to a song and open the result in DaVinci Resolve. [A rough cut on the beat](#a-rough-cut-on-the-beat-straight-to-resolve). - **Motion graphics.** Titles, lower thirds, kinetic type, captions, slideshows and music videos, rendered on this machine with HyperFrames. [Motion graphics](#motion-graphics-titles-captions-slideshows-and-music-videos). - **Finish.** The final cut and mix through Tesseract, to Resolve, Premiere or After Effects. [Finish](#finish-the-final-cut-and-mix-to-resolve-premiere-or-after-effects). - **Work on a clip you already have.** *Restyle*, *Extend* and *Fix*, under every clip in the Library. [Restyle, Extend, Fix](#work-on-a-clip-you-already-have-restyle-extend-fix). - **Train your own LoRA.** A look or a character from your own clips, on the CUDA build (a DGX Spark or a GPU). [Train your own LoRA](#train-your-own-lora). - **Captions and search in the Library.** The studio describes every clip and picture, so you can search by what is in them. [Search your library](#search-your-library-by-what-is-in-it). - **Previs and prompts into a film.** What the filmmaker suite made goes straight into a film's shots. [Bring previs and prompts into a film](#bring-previs-and-prompts-into-a-film). Also new: - **The installer asks what to install.** Run in a terminal, the one-click install first shows a checklist of every part this machine can run, each with its download size and the free space, everything ticked. Untick what you do not want and press Enter; `--yes` skips the list and installs the usual set. Parts behind a licence on Hugging Face (FLUX3-K, Stable Audio Open) are checked before anything downloads: one you have not accepted yet is left out with the page to accept it, and the rest installs. [Install](#install). - **Bolt is always one of your agents** when the studio's own engine is installed, beside a local brain on another server (a GLM, a DeepSeek) and your subscriptions: nothing to add by hand. - **Looks:** **Realism People** by fal (trigger `r34l1sm`) for faces that hold up in close-up, and Keys' newer LoRA pack (r4: 22 LoRAs from one hosted copy); an install with an older pack is brought up to it. Any LoRA, one you trained included, can show a cover picture kept beside it (`loras/.webp` in the studio folder). **3D Animation Style is no longer offered**: on the DGX Spark's FastH3 it barely changed a clip, even at full strength. - **Library search** gives each match its own tick box, and *Select all* takes the matches while you search, so a trailer, a finish or a delete can use just the clips you found. - **Restyle and Fix keep a clip's motion and change its look**: the first frame is redrawn (in the new style, or with the fix), Motion Previs reads the clip's depth, and the clip is rendered again from both. - **Motion graphics run only on a Node.js with the latest security fixes** (the 2026-10-21 release: 22.23.4, 24.22.1, 26.12.1); an older one, the system's or the studio's own, is replaced. - **Your phone and other devices, with an access key.** On the computer running the studio nothing is asked. A phone, a tablet or another computer signs in once: **Settings → Access key** shows a QR code that signs a phone in with one scan, the key itself to copy, and *Make a new key* for a lost phone. Step by step: TUTORIAL.md section 15, and **Learn → Your phone and other devices** in the app. Details: [The access key](#using-it-on-your-phone). - **Automatic updates are on by default**, so an install stays on the latest version: checked every six hours, installed when the queue is idle (Settings → Updates turns it off). Worker Sparks follow their head. - **NVIDIA RTX Pro 6000 Blackwell (96 GB)** uses the same one-shot recipe: engines built for sm_120, the card's own memory read for every job, Bolt through llama.cpp ([docs/rtx-pro-6000.md](docs/rtx-pro-6000.md)). - **Every credit, in the app and in the repo.** Settings → About → *Every credit* shows CREDITS.md and the third-party notices; about 25 more libraries, conversions and tools are credited, and a test keeps the two in step. - **No personal information in the repo**, enforced by a test and by CI; contact goes through GitHub only (Issues, Discussions, private vulnerability reporting: [SECURITY.md](SECURITY.md)). - **The repository is now `drowzeys/Imagination-Studio-by-keys-Wasserman`**, matching the name. The old address redirects, so installs that follow it keep updating. ### Bring previs and prompts into a film What the filmmaker suite made goes straight into your work, not only into a new film. - **Into a film you already have.** On the **Suite** page, open anything you brought in (a Blockout export, a Motion Previs pack, a board, a screenplay) and use **Send into a film**. Pick the film, say which of its shots each imported shot goes to (or *A new shot*, in a scene you choose), and press **Attach**. The still becomes the shot's first frame, a previs clip becomes its motion reference when the shot is rendered, and the shot's words fill its prompt when that is empty (a prompt you wrote is never replaced). A previs reference clip is sent to the Ref2VA model, so it needs `oneshot-setup.sh --ref2va`; it does not combine with a first frame, and the shot's check says so. - **Into the Home composer.** **From the suite**, beside *Ask your agent instead*, lists what you brought in and its shots. Pick one and the box fills with its words, the length is set to the nearest the model makes, and its still is the clip's first frame (remove it with the small button if you only want the words). - **Recent imports on the Suite page** show their app's icon with *Send into a film* and *Use in the composer*. - **Your agent can do it too.** `suite_imports` lists what was brought in and its shots; `suite_attach` sends shots into a film. It runs on this computer and needs no account. ## New in 3.1.0 (2026-10-20) - **One-shot installs with everything, on both machines.** `bash oneshot-setup.sh --all` installs every feature the machine runs, from prebuilt engines: the GHCR carrier `keys-tensorfold-studio:3.1.0` (the DGX Spark's H3, Music 3, Drama 3 and Bolt engines) and the v3.1.0 release (the Mac's native engine and Bolt on Metal), each checksummed. The minimum hardware now opens this README. Proven on a fresh DGX Spark and a fresh Mac. - **NVIDIA Nemotron 3.5 Bolt is the studio's own brain**, loaded and unloaded by the Studio around renders (TensorFold's CUDA engine on a DGX Spark, Metal on a Mac), for the First AD, the agents and the local harness. - **A Mac can lead worker DGX Sparks**, and a DGX Spark head leaves clips to its idle workers. - **Voice on a DGX Spark in one shot** (Drama 3's CUDA engine from the carrier), and sound effects and foley measured and guarded there. - **A LoRA on the Mac's native FastH3 engine**, merged into the int8 weights at load; sound effects on Metal twice as fast. - **The Mac's memory read correctly** (free, file-backed and purgeable pages), so a large job no longer pushes the Mac into a memory panic, and every ComfyUI job's peak is checked first. - Upscale, Find and Remove a person and MOSS-Image on a Mac (below), updates that refresh the renderers' copies, and Sam Wasserman's CI, host-independent tests and fixes. ### Upscale, Find and Remove a person on a Mac - **Upscale, Find a person and Remove a person now run on a Mac** (Mac Studio M5 Ultra), with the DGX Spark's own graphs and the authors' node packs at the Spark's pins, on a ComfyUI of the Mac's own on PyTorch's Metal device (`oneshot-setup.sh --upscale` and `--remove`; `scripts/setup-comfy.sh`). MiniMax H3 is read from the Mac's own Ref2VA files (`--ref2va`), not downloaded again: 2.4 GB of models and a 1.6 GB environment. Measured against the DGX Spark on the same clip and seeds: see [On a Mac](#on-a-mac-the-same-graphs-on-pytorchs-metal-device). - **MOSS-Image on a Mac** (`oneshot-setup.sh --moss`, 28 GB) on the same ComfyUI: 59 s a 1344x768 picture with the model loaded (44 s on a DGX Spark), the lettering as exact as on a Spark. ![MOSS-Image: DGX Spark left, Mac right, same prompt and seed](docs/shootout/moss-mac-spark-poster.jpg) ![MOSS-Image: DGX Spark left, Mac right, same prompt and seed](docs/shootout/moss-mac-spark-samurai.jpg) ## New in 3.0.1 - **MOSS-Image**, a fifth picture model, on a DGX Spark or a GPU: OpenMOSS's 5B model (Apache 2.0, commercial use allowed), through ComfyUI's own support. It is the best of the picture models at lettering: posters, title cards, signs and menus came out exactly as written, in English and Japanese. About 45 s for 1344x768. Install it with `oneshot-setup-spark.sh --moss` on a DGX Spark, or `oneshot-setup-rtx-6000.sh --moss` on a GPU (30 GB; `--moss` is in the CUDA setup both doors run); see [MOSS-Image on a DGX Spark](#moss-image-on-a-dgx-spark). - **ComfyUI moves to v0.42.0** (`b9b290e4`) on a DGX Spark, for MOSS-Image: `oneshot-setup-spark.sh --upgrade` moves an existing install. FLUX3-K, Qwen and FastH3 clips render as before on it. - **Credits:** OpenMOSS in the README, CREDITS.md, NOTICE and the app's About. - Nothing changes on a Mac: MOSS-Image has no Mac build yet. ## New in 3.0 (since 2.9.3) **A new studio around the same engines** - **Imagination Studio**: a new look (the poster's serif, blue, green and amber; Geist and Instrument Serif), Sam's art on every engine, card and look, and a sidebar of **Creative Engines** and **Studio Vaults** with Home on top. - **Six engines, one page each**: Story, Image, Video, Previs, Sound and Edit, each opening on its own cards. - **Home**: one composer makes a clip, a picture, a voice line or a song straight away; start cards, recent takes, the machine in one line. - **Settings**: the look, which rooms show, what a new take starts from, every connection, and Models & weights, Sparks, Brains & tools and Updates in one place. - **Made for phones too**: the whole studio works from a phone's browser. The six engines sit along a bottom bar, a **More** sheet holds the Agent, the vaults, Settings, search and updates, the queue folds into one line above it, and *Add to Home Screen* opens it full screen like an app. See [Using it on your phone](#using-it-on-your-phone). **Agents and your own sign-ins** - **The Agent room**: pick Claude Code, Codex, Hermes, Gemini, Grok or a local model and talk it through; it keeps its own session, asks when a choice matters and drives the studio's tools (stills, clips, voice, songs, the timeline), Blender and Blockout. - **One-click Connect** for every agent, from any page that needs one: finds it or installs it, runs its own sign-in (the page opens by itself, with the code, a QR code and Copy link), checks it answers, makes it the default. - **Link another computer**: a DGX Spark, or a GPU, uses the agents signed in on your Mac over SSH, and the studio sets up both directions itself. Claude Code's Mac Keychain over SSH is solved with **Allow over SSH** (`BRAINS.md`). - The ChatGPT app's own Codex is used when the one on PATH is older, here or over SSH. **Models, looks and installs** - **Models & weights** finds every model the studio needs anywhere on the machine and links it instead of downloading it again (**Use everything found here**), and **Install** runs the right installer for what is missing. - **Gated weights** (Keyframe Animation, Stable Audio Open): Install signs you in to Hugging Face once, opens the licence page, and downloads by itself once access is granted. - **Looks**: the LoRA hub as a style library, one card a look. **Sound, previs and the library** - **Sound effects and foley**: MMAudio makes a clip's own sound from its picture, Stable Audio Open makes a sound from words (Mac; on a DGX Spark through ComfyUI once its memory is measured). - **Motion Previs from a link**: paste a video page or a video file's address and its depth, pose and camera are read. - **The Library** lists voice lines, songs and sound effects beside clips and stills, each with its own filter. - **Nodes**: a ComfyUI-style canvas over the studio's own jobs, run in order. - **Wasserman's Filmmaker Suite** built in: bring work in from Sam's apps, send the cut to Resolve as a ZIP with media. **Machine, updates and safety** - **Machine**: live GPU, memory, temperature, power and storage; Sparks; an optional workload governor for a shared Spark. - **Updates** in the app: follows the installed branch, waits for idle, shows the latest release. - Everything is kept in **`~/ImaginationStudio`** (an older `~/TensorFoldStudio` is moved there once, with a link left behind). - Tighter safety: this computer or its Tailscale name only, same-origin writes, size limits, no hidden files served. ## Screenshots | | | |---|---| | ![Home: the composer, start cards and the machine](docs/screenshots/home.webp) | ![Story: the film board, Wasserman Slate, First AD](docs/screenshots/story.webp) | | **Home** | **Story** | | ![Image: stills, scouts, edits and looks](docs/screenshots/image.webp) | ![Video: clips with sound, long takes, references, Sharp Spec](docs/screenshots/video.webp) | | **Image** | **Video** | | ![Previs: Motion Previs and your agent](docs/screenshots/previs.webp) | ![Sound: voice lines and songs](docs/screenshots/sound.webp) | | **Previs** | **Sound** | | ![Edit: timeline, Resolve, dailies, Library](docs/screenshots/edit.webp) | ![Nodes: a canvas over the studio's jobs](docs/screenshots/nodes.webp) | | **Edit** | **Nodes** | | ![Looks: the LoRA hub as a style library](docs/screenshots/looks.webp) | ![Looks: one card a look, with its licence and size](docs/screenshots/looks-cards.webp) | | **Looks** | **Looks, the cards** | | ![Wasserman's Filmmaker Suite, built in](docs/screenshots/filmmaker-suite.webp) | ![Machine: live readings and storage on a DGX Spark](docs/screenshots/machine.png) | | **Wasserman's Filmmaker Suite** | **Machine** | ## Using it on your phone The models run on your Mac or DGX Spark; the phone is only the screen and the remote. Everything works from it: write a prompt, roll a clip, watch the queue, play takes from the Library, plan in Story, talk to your agent. 1. **Put the computer and the phone on the same [Tailscale](https://tailscale.com) network** (free for personal use): install Tailscale on both and sign in with the same account. 2. **Share the studio on that network**, once, on the computer running it: ```bash tailscale serve --bg 7870 ``` Tailscale prints the address, `https://..ts.net`. Only devices on your own tailnet can open it, over HTTPS, and the studio accepts its Tailscale name by itself. (On a trusted home network, `HOST=0.0.0.0 bash scripts/app.sh` and `http://:7870` work too; the access key below still guards it, but keep that to a network you trust.) 3. **Sign the phone in, once:** on the computer, open **Settings → Access key** and scan its QR code with the phone's camera. It opens the studio on the phone, signed in. The phone keeps it. Then *Share → Add to Home Screen* (iPhone) or *⋮ → Add to Home screen* (Android): the studio opens full screen from its own icon. **The access key.** On the computer running the studio nothing is asked: open `http://localhost:7870` (or the computer's own network address or `.local` name, when it is opened to the network) and use it, updates included. Reaching it from another device (a phone over Tailscale, another computer on the network) takes the studio's access key, once per device: on the computer, **Settings → Access key** shows a QR code that signs a phone in with one scan, and the key itself to copy. The key is made once, kept in `access-key` in the studio folder (readable by you only) and survives restarts and updates; *Make a new key* there signs every other device out. `bash scripts/studio-key.sh` prints the same from a terminal. A clip read from another machine's app (Slate's *Import a take*) needs that app's key beside its address. On the phone: - **Bottom bar**: Story, Image, Video, Previs, Sound and Edit, one tap each. **More** opens the Agent, the Library, Looks, Wasserman's Filmmaker Suite, Nodes, Machine, Learn, Settings, search and updates. - **Home** keeps the composer on top: pick *Clip with sound*, *Picture*, *Voice* or *Song*, describe it, *Generate*. - **The queue** is the line above the bar; tap the arrow to see what is rolling and what is next. Renders keep going on the computer when the phone sleeps or leaves. - **Connect an agent from the phone**: *Connect* shows the sign-in code in large type with *Copy code* and *Copy link*, and a QR code; sign in on the phone itself, or scan the QR with another device. | ![Home on a phone: the composer, the queue line and the bottom bar](docs/screenshots/phone-home.webp) | ![The More sheet: the Agent, the vaults, Settings, search and updates](docs/screenshots/phone-more.webp) | ![The Video engine on a phone](docs/screenshots/phone-video.webp) | | :---: | :---: | :---: | | **Home** | **More** | **An engine: Video** | ## Contact Through GitHub only; the project publishes no email address. Bugs and install trouble: **Issues**. Questions, ideas and what you made: **Discussions**. A security problem: the **Security** tab's *Report a vulnerability*, which only the maintainers see ([SECURITY.md](SECURITY.md)). Never paste a token, key or your own address into any of them. ## License & credits Everyone whose work the studio uses is named in [CREDITS.md](CREDITS.md) (with [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)), and the app shows the same files under Settings → About → Every credit. **Apache License 2.0** — see [LICENSE](LICENSE). Free to use, modify, fork and build on, commercially or otherwise; the models it downloads keep their own licences (below). **Attribution required:** per the [NOTICE](NOTICE) file (Apache 2.0 §4(d)), any use, fork or redistribution must retain the NOTICE file and credit **Keys ([github.com/drowzeys](https://github.com/drowzeys))** and **Sam Wasserman / Wasserman Productions ([wassermanproductions.com](https://wassermanproductions.com))** in its documentation and about/credits surface. To cite it: [CITATION.cff](CITATION.cff). The same credits the studio shows in Settings → About; [CREDITS.md](CREDITS.md) has each one in full. - **Keys (drowzeys)** — the studio itself, its TensorFold families and int8 kernels, and its Mac and DGX Spark engines. - **Sam Wasserman and Wasserman Productions** — helped design and build the studio in collaboration with Keys; Wasserman's Filmmaker Suite (ScriptBreak, Cork Board, Master Canvas, Storyboard Reference Studio, Blockout, Motion Previs Studio, Slate, Circle Take, Stem Studio, Call Sheet, DaVinci MCP), the Sharp Spec, the 80s Horror LoRA and the studio's art. - **Qwen team** — Qwen-Image-2.1. **Black Forest Labs** — FLUX 3 Image Klein (FLUX3-K). **OpenMOSS** — MOSS-Image. **MiniMax** — MiniMax H3 and MiniMax-Music3. **Built with Fish Audio** — Fish Audio Drama 3. - **Stability AI** — Stable Audio Open · **Ho Kei Cheng et al.** — MMAudio · **kijai** — ComfyUI-MMAudio (sound effects and foley). - **Ash Hart** — TensorFold. - **antirez** — h3.c · **RobZombAI** — H3MLX · **mrbizarro** — minimax-h3-mlx (Phosphene) · **Filip Strand and the mflux contributors** · **felix-ab** — flux3-mlx. - **Comfy Org** — ComfyUI · **Apple** — MLX · **NVIDIA Research** · **FastVideo** · **LightX2V** · **Viggle** · **speach1sdef178** · **rookiestar28** · **ddalcu**. - **The agents you connect** — Claude Code (Anthropic), Codex (OpenAI), Gemini CLI (Google), Grok CLI (xAI), Hermes Agent (Nous Research); Ollama, LM Studio, vLLM, llama.cpp; Blender and blender-mcp. - OpenAI **Whisper** and **faster-whisper**, **yt-dlp**, **FFmpeg**, **PyTorch**, Hugging Face **diffusers** and **transformers**, **FastAPI**, **Geist** and **Instrument Serif**. - **The community LoRA authors**, by name in Looks and in [CREDITS.md](CREDITS.md). **Licences.** The studio's code is Apache-2.0. The models keep their own terms; do not redistribute their weights. Qwen-Image-2.1: Qwen Research License (non-commercial) · MiniMax H3: MiniMax H3 Community License (territory limits) · MiniMax-Music3: its licence asks for its name in a commercial UI · FLUX3-K: FLUX Kommunity License · MOSS-Image: Apache 2.0 (commercial use allowed) · Fish Audio Drama 3: Fish Audio Community License v1.0 (a hosted voice service needs Fish's own licence) · Stable Audio Open 1.0: Stability AI Community License (free under US$1 million of yearly revenue) · MMAudio's weights: CC-BY-NC 4.0, non-commercial · Wasserman's Filmmaker Suite: each app under its own licence, Apache-2.0 (Master Canvas: MIT). **Links.** Keys: [X @u1tra_instinct](https://x.com/u1tra_instinct) · [GitHub](https://github.com/drowzeys). Sam Wasserman / Wasserman Productions: [wassermanproductions.com](https://wassermanproductions.com) · [X @SamJWasserman](https://x.com/SamJWasserman) · [GitHub](https://github.com/wassermanproductions). **Powered by** TensorFold (Ash Hart and the TensorFold contributors) · built with integrated Fish Audio Drama 3 · Built with Qwen · Built with Fish Audio · MiniMax-Music3. ## New in 2.9.3 - **FLUX3-K installs again on a DGX Spark.** On 2026-10-17 Black Forest Labs renamed the Klein checkpoints on Hugging Face (`flux3-image-klein-bf16.safetensors`, `flux3-image-klein-base-bf16.safetensors`) and removed the old underscore names, so `oneshot-setup-spark.sh --flux3` could not download them. The setup now fetches the new names and renames an older install's files in place instead of fetching 22 GB again: they are the same files (the same SHA-256 on Hugging Face). The app reads either name, so a Spark updated from the Sparks tab keeps FLUX3-K before the setup runs. Nothing changes on a Mac. ## New in 2.9.2 - **Mac: Music runs on TensorFold's native Metal engine.** On an M5 or later the Music tab now runs MiniMax Music 3 on `music3-generate` built for Metal (Zig and Metal, no Python or MLX; TensorFold pull request #987, commit 8c9559da), when `oneshot-setup.sh --music` has built it (`scripts/build-music3-metal.sh`). On the Mac Studio M5 Ultra, against the MLX port int8, 60 s hip-hop, seed 7, three runs each: the autoregressive model 69 frames/s against 65, the whole song 59.5 to 63.0 s against 62.3 to 63.9 s, peak 17.8 GB against 28.1 GB. Through the app a song the model ended at 33.4 s took 35.2 s, load included, at 16.1 GB. Limits: it does not stream yet, so *Play while it's made* is MLX-only (`MUSIC3_BACKEND=mlx`); an M1 to M4 keeps the MLX port; measured on an M5 Ultra only. See [Music](#music-minimax-music-3). ## Imagination Studio 3.0: the studio The web app is **Imagination Studio by keys+Wasserman**, powered by TensorFold and built with Fish Audio Drama 3. Everything it did before is still here, arranged to be quicker to find and calmer to use. - **Creative Engines.** Video (clips with sound, long takes, references), Image (stills, scouts, edit a picture), Sound (Voice, Music, Foley), Story (the film board, Wasserman Slate, the First AD), Cast & Places, Director, Previs (Motion Previs, Ask your agent), Motion graphics, Edit (the Timeline, Graphics, Finish and its exports) and Night shift. Each opens on a page of picture cards; a card opens the view that does it, already set up. In Create a small switch at the top says where the prompt goes: video with sound, an image, or an edit of a picture. - **Studio Vaults.** Library (every take), Looks (the LoRA hub as a library of styles, one card each), Suite (bring work in from the filmmaker suite, send the cut to Resolve and dailies to Circle Take), Machine (telemetry, Sparks, brains and tools, updates) and Learn. - **Home**, under the wordmark, starts the common jobs in one click and shows recent takes and the machine. - **Updates.** Machine → Updates shows the installed version and commit, the branch it follows, the newest GitHub release (for information) and one *Update and restart* button. Installing is always a fast-forward of the followed branch, and waits until jobs and the render engine are idle. - **Render bay and ⌘K.** The queue is docked along the bottom of every room. ⌘K (or `/`) searches and runs anything; ⌥1 to ⌥9 and ⌥0 jump between rooms. Below 1100 px the sidebar is an icon rail; on a phone the rooms sit along the bottom. - **Pictures.** The cards' key art fills from `app/static/art`: each slot is listed in `art/ART-MANIFEST.json` and shows a plain panel until its picture is there. Model and tool marks in `app/static/brand` identify what the Studio runs and belong to their owners (`brand/THIRD-PARTY-MARKS.md`). The typefaces (Geist, Geist Mono, Instrument Serif) ship in `app/static/fonts` under the SIL Open Font License. ## New in 2.9.1 Three changes to Music (MiniMax-Music3), one for each machine and one for the Mac's player. Every figure is one run unless it says otherwise. - **Mac: int8 weights by default.** The Studio starts the MLX engine (`music3-mlx`, now pinned at 6844b413) with `--quant int8`: the autoregressive model, its code-0 head and the depth decoder at 8 bits. On the Mac Studio M5 Ultra the autoregressive model went from 40 to 65 frames a second and the peak from 27.6 to 20.5 GB; a 60 s song took about 63 s through the app (75.8 s in 2.9.0). Against ComfyUI's reference the code-0 logits keep cosine 0.99994 (bf16 0.99997), 98.0 % greedy agreement, and Whisper's lyric scores over six same-seed songs stayed within their noise. `MUSIC3_QUANT=none` runs the bf16 weights. Limits: measured on an M5 Ultra only; other M-series chips are not measured. - **Mac: play while it's made.** With the box ticked (the default where the engine streams) the song plays in the tab while it is made, from `GET /api/music/takes/{id}/live`, one stereo 44.1 kHz WAV. The sound is held back until playing will not run dry (the engine's measured no-stall start plus 3 s). Through the app, 60 s songs: first sound at about 7 s (7.6 and 6.4 s), playing from about 13 s (13.4 s both), no stall (the least buffer 4.0 s), made in 63.4 and 61.0 s; a 30 s song played from 8.1 s. The kept take is the engine's finished WAV, the same samples that were played. Limits: a Mac only (no route on a Spark streams); only 30 s and 60 s were measured through the app, and the wait grows with the length (about 56 s for a 300 s song, from the engine's own figure); nobody has listened for seams. Details in [Music](#music-minimax-music-3). - **Music on a DGX Spark: TensorFold's CUDA engine for MiniMax Music 3 (MiniMax-Music3).** The Music tab on a Spark now runs `music3-generate`, TensorFold's native CUDA port (branch `music3-cuda`, 6b714531: Zig + CUDA, no Python at run time; since 3.3.0 the same family on TensorFold 1.1.5, branch `music3-cuda-115`, 5505dbfa), wherever it is built and the weights are there; otherwise it falls back to ComfyUI's nodes as in 2.9.0. `MUSIC3_BACKEND=cuda|comfy` chooses; the tab's state says which route runs and, after a fall-back, why. - **int8 weights by default**, made on the GPU as the engine loads (no cache file): the autoregressive model, its head and the depth decoder at 8 bits. `MUSIC3_WEIGHTS=bf16` runs the checkpoint's own weights. It reads Comfy-Org's three files as they are (`~/spark-studio-models/MiniMax-Music3-Comfy`, `MUSIC3_MODEL` names another). - **Same lifecycle as the Mac engine:** a persistent server, loaded on the first song, holding `~/studio-gpu.lock.d` while loaded, unloaded after five idle minutes and before any other job, under the memory kill-guard; a studio ComfyUI that holds models is stopped before it loads. Never beside H3, Drama 3 or ComfyUI's models. - **Installed by** `bash oneshot-setup-spark.sh --music` (and `--upgrade` where the weights are): `spark/build-music3.sh` takes the prebuilt binary from the carrier image, or builds it with nvcc 12.9+ and Zig 0.17 (50 s on a GB10). - **Measured on one GB10** (int8, hip-hop lyrics, seed 7, 30 steps; one run each; memory is MemTotal - MemAvailable above the idle machine): | Song | TensorFold CUDA (int8) | ComfyUI's nodes (2.9.0) | |---|---|---| | 30 s, engine | 60.3 s | 131.0 s | | 60 s, engine | **123.9 s** (AR 73.9 s at 20.4 frames/s, DiT 46.4 s, DAV 3.6 s) | 260.6 s | | 120 s, engine | 252.0 s | 527.6 s | | 60 s through the app | **149.2 s** for the song (load 37.3 s, AR 68.2 s, DiT 40.4 s, DAV 3.3 s; the model ended it at 55.2 s), 171.4 s with Whisper's check (97 %, 164 of 169 words) | 270.6 s for the song (ComfyUI started for it; AR 166.5 s, DiT 94.3 s, DAV 4.0 s; 59.99 s made), 297.0 s with Whisper (96 %, 162 of 169), 29.4 GB at the peak: the same run of the 2.9.1 app with `MUSIC3_BACKEND=comfy` | | Peak memory, 60 s | 17.9 GB (18.7 GB through the app) | 29.3 GB | bf16 weights: a 60 s song in 194.3 s and 25.5 GB. Parity against ComfyUI's nodes and Whisper's lyric count passed on the engine branch (docs/recipes/minimax-music3-cuda.md there). - **Honest limits.** No streaming on the Spark: a song is heard when it is done. Built and measured on a GB10 only (the build refuses another GPU unless `MUSIC3_ANY_GPU=1`; elsewhere use `MUSIC3_BACKEND=comfy`). Loading takes about 37 s each time the engine starts (int8 is made at load), so the first song after five idle minutes pays it again. Songs over 120 s are not measured on this engine; the memory estimate goes on at the measured slope. - **Carrier image** `ghcr.io/drowzeys/keys-tensorfold-studio:2.9.1` adds the prebuilt Spark `music3-generate` (`spark-music3/`) beside the H3 engine. ## New in 2.9.0 - **Music: MiniMax Music 3 (MiniMax-Music3).** A new **Music** tab beside Voice makes a whole song with sung vocals from your lyrics and a style, on this machine: up to five minutes, 44.1 kHz stereo. See [Music (MiniMax Music 3)](#music-minimax-music-3). - **Lyrics** take section tags (`[Intro]`, `[Verse]`, `[Pre-Chorus]`, `[Chorus]`, `[Bridge]`, `[Instrumental]`, `[Outro]` ...) on lines of their own; the tab has a button for each and says when a tag looks wrong. - **Style** is a caption: genre, tempo, key, mood, vocals, instruments. A helper writes one from those six boxes. - **Use as soundtrack:** a song can be laid under a long video, the Editor's export or any clip of the Library. It sits under the clip's own sound, is lowered up to 12 dB while someone speaks, and no peak passes -1 dBFS. - **Mac:** TensorFold's MLX port (`music3-mlx`, pinned at 6844b413), installed by `bash oneshot-setup.sh --music` into its own environment, with the 22 GB of weights. **DGX Spark:** ComfyUI's own MiniMax Music 3 nodes, `bash oneshot-setup-spark.sh --music`. - **Measured through the app, one 60 s song on each machine** (the same pop lyrics and style, seed 7, 30 steps; one run each): | 60 s song | Mac Studio M5 Ultra (MLX) | DGX Spark (ComfyUI, CUDA) | |---|---|---| | Whole job, song made | **75.8 s** (59.99 s of song), files in the page cache | **289.0 s** (59.99 s of song), ComfyUI started and the files read from an empty page cache | | Words and melody (autoregressive model) | 37.8 s | 176.3 s | | Sound (DiT, 30 steps) | 34.6 s | 96.3 s | | Decoder | 2.0 s | 4.1 s | | Model load | 0.9 s (first song) | inside the stages above (ComfyUI loads as it goes); about 12 s more than with the files cached | | Peak memory | 27.6 GB (MLX's own peak); the machine's in use rose 32.9 GB | 29.3 GB taken from the machine (MemTotal - MemAvailable, 5.1 GB before) | | Whisper on the take | 96 % of the words sung (128 of 134); the song reached 43 % of the lyrics at 60 s | 1 %: on this take Whisper heard only "We'll be right back" twice. A second take through the app (hip-hop lyrics, seed 11, 273.2 s): 98 % (166 of 169), all the lyrics reached | - **Honest limits** (as of 2.9.0; 2.9.1 plays a song while it is made on a Mac and adds TensorFold's CUDA engine on a Spark, above). A song is not played while it is made (no streaming yet). Whisper's count on sung words is rough, and one Spark take in three was barely heard by it (above). Lengths measured: on the Spark 30, 60, 120 and 300 s asked (one run each; the 300 s one ended itself at 189.8 s, so no song over 120 s was made to its full length there); on the Mac songs of 10, 30 and 60 s. Other lengths are untested, and so is anything but the default settings. On the Spark the autoregressive stage runs in ComfyUI's Python loop at about 9 frames a second, so a song takes about 4.4 times its length there against 1.25 on the Mac. - **Upgrade** with `git pull`, then `bash oneshot-setup.sh --upgrade --music` (on either machine) to add the Music tab's engine and weights; restart the app. - **Licence:** MiniMax-Music3 Community License. Commercial use is allowed, but a commercial product must show "MiniMax-Music3" in its interface (the tab does). Above US$20 million of yearly revenue you need MiniMax's written authorisation, and a hosted service must keep safeguards against misuse. ## New in 2.8.0 and 2.8.1 - **2.8.1: Qwen 2.1 Full.** The picker now offers Qwen-Image-2.1 in two versions, as it does FLUX3-K: **Qwen 2.1 Turbo** (8 steps, the quickest) and **Qwen 2.1 Full**. - **What Full is:** the 40-step base model with true guidance (CFG 4 with an empty negative prompt, as Qwen's own pipeline runs it). It gives more detail than Turbo. - **Where it is offered:** wherever the base weights are installed, which is the same files Edit a picture's 40-step choice uses (`oneshot-setup.sh --image-edit`). - **Mac:** the Mac's generator gained `--cfg` for it. - **DGX Spark:** it runs ComfyUI's 40-step CFG-4 workflow. - **Times on an M5 Ultra at 1344x768:** Turbo 12 s, Full 47 s, both peaking at 15.3 GiB. Full run without guidance took 24 s and was barely better than Turbo, so guidance stays on. - **A shootout of all four picture models, Mac against DGX Spark**, with 128 pictures, says which to use for what: see [Which picture model to use: the evidence](#which-picture-model-to-use-the-evidence). **[▶ Watch the 60 s demo](https://github.com/drowzeys/Imagination-Studio-by-keys-Wasserman/releases/download/v2.8.0/wasserman-studio-mac-demo.mp4)**: the Studio on one Mac Studio M5 Ultra, recorded live on 2026-10-16. It shows the app at work, two clips with sound, and the times and memory measured during those renders. ![Two demo clips made on the M5 Ultra: a FLUX3-K Fast picture to 864x480 video, a Qwen 2.1 Turbo picture to 1344x768 video](samples/demo_mac_280.jpg) - **Pick the picture model.** Wherever the Studio makes a picture you can choose **Qwen 2.1 Turbo** (the quickest), **FLUX3-K Fast** (the distilled checkpoint, 16 steps) or **FLUX3-K Full** (the base checkpoint, 50 steps at CFG 3.5). That covers *Image only*, scouts, the first frame of a clip and a last frame. Install them with `bash oneshot-setup.sh --flux3` (41 GB); first accept the FLUX Kommunity License on Hugging Face. - **How each prompt is handled:** BFL's prompt upsampler writes a full scene caption from your prompt first. - **On a DGX Spark:** FLUX3-K runs on ComfyUI's own FLUX 3 nodes, with ComfyUI moving to `5a665890`. - **On a Mac:** it runs on flux3-mlx in an environment of its own. - **Edit a picture with FLUX3-K.** It also takes up to nine further reference pictures, for example a person from one picture put into the place of another. The LoRAs, the camera ring and the 40-step base model stay Qwen's. - **Better text and whole figures.** On the same prompts and seeds, FLUX3-K: - spelled a poster's title and tagline where Qwen wrote "SOME ONE IS STOLL LISTENNING"; - drew whole figures for character sheets where Qwen cut them at the waist; - kept two characters recognisable in a two-reference shot. - **New carrier image.** `ghcr.io/drowzeys/keys-tensorfold-studio:2.8.0` now holds the DGX Spark's LoRA engine (`h3-cuda-lora` `1f7ecc75`), so a Spark install no longer builds it from source. - **Upgrade** with `git pull`, then `bash oneshot-setup.sh --upgrade`. Add `--flux3` for the FLUX3-K weights. ![FLUX3-K against Qwen 2.1 Turbo on the same poster prompt and two seeds](samples/flux3k_vs_qwen_poster.jpg) *The poster prompt, two seeds. Left: Qwen 2.1 Turbo, on the Mac. Middle and right: FLUX3-K Full ("Base") and FLUX3-K Fast ("Distilled"), here run through diffusers on a Spark, so the times are diffusers' (91 s and 15.5 s). In the Studio, through ComfyUI, they take 57.5 s and 10.5 s.* **Picture times**, 1344x768, model loaded, bf16: | | DGX Spark | Mac Studio M5 Ultra | |---|---|---| | Qwen 2.1 Turbo, 8 steps | 9.5 s | 9.7 s | | Qwen 2.1 Full, 40 steps at CFG 4 (2.8.1) | | 47 s (whole run, model load included) | | FLUX3-K Fast, 16 steps | 10.3 to 10.7 s | 14.9 s | | FLUX3-K Full, 50 steps | 57.5 s | 53.6 s | | FLUX3-K prompt upsampler, once per prompt | about 18 s | 5 s | | Edit a picture: Qwen / FLUX3-K Fast / FLUX3-K Full | 24 / 37 / 139 s | 10 / 37 / 240 s | | Two-picture edit: Qwen / Fast / Full | 23 / 44 / 188 s | 13 / 42 / 427 s | FLUX3-K Full is slow for edits on a Mac, so Fast is the FLUX3-K to use there. **A whole clip, text to picture to 5 s of video with sound**, measured through the app. On the Mac these are the demo renders; on the Spark, the 2.8.0 tests. | | Picture | Video (FastH3, 8 passes) | Total | Peak memory of the render | |---|---|---|---|---| | Mac, 864x480, FLUX3-K Fast | 11 s | 70 s (5.93 s a pass) | **81 s** | 29.0 GB | | Mac, 1344x768, Qwen 2.1 Turbo | 11 s | 201 s (20.1 s a pass) | **212 s** | 33.0 GB | | DGX Spark, 864x480, FLUX3-K Fast | about 32 s (upsampler included) | 70 to 96 s | **102 to 128 s** | | | DGX Spark, 864x480, FLUX3-K Full | 53 s | 96 s | **149 s** | | Peak memory is the render process's macOS footprint, GPU memory included, sampled every second. The peak comes at the handover from the picture model. FastH3 itself holds 23 GB at 864x480 and 27 GB at 1344x768. ![Memory of the render process during the two demo renders on the M5 Ultra](samples/demo_mac_280_memory.png) **The Studio's TensorFold work, submitted upstream.** At the TensorFold project's invitation, the engines behind this Studio are open as pull requests to [ashhart/TensorFold](https://github.com/ashhart/TensorFold/pulls?q=author%3Adrowzeys). Each lists what was tested and what was not: | Pull request | What | |---|---| | #949 #950 #959 #960 #961 | MiniMax H3 / FastH3 on Metal in bf16 (10.2 s a pass against 27.9 s for MLX bf16), from an image, any step count, re-voicing, Ref2VA | | #947 #958 #946 | MiniMax H3 / FastH3 on CUDA, one GPU or one pass split over several DGX Sparks (3 Sparks: 3.0 s a pass in int8, 5.0 s in bf16), and the runtime hook it uses | | #939 #956 | Fish Audio Drama 3 on CUDA (34 frames/s) and on Metal (84 frames/s, first audio 0.15 s, 9.5 GB) | | #948 | Qwen-Image 2.1 on Metal (0.74 s a step, 43 dB PSNR against mflux) | | #941 to #944 | This Studio: Slate, Motion Previs, FLUX3-K and Voice | These are proposals: this repository's install still pins the engines on the `drowzeys/TensorFold` fork. ## Big credit: Sam Wasserman and Wasserman Productions (wassermanproductions.com) **Sam Wasserman shaped the production side of this studio: the AI film workflow and the ideas behind it.** Slate, his prompt studio for AI filmmaking, is the workflow inside the Wasserman Slate AI Studio tab. The Wasserman Sharp Spec, the QA gate that follows it, and the 80s Horror LoRA that opens the Keys and Wasserman LoRA Hub are his too. The Motion Previs tab's analysis is a Python port of his [Motion Previs Studio](https://github.com/wassermanproductions/motion-previs-studio). The aim we share is the best AI film studio there is, and this pack is where his planning tools meet a render engine. See [CREDITS.md](CREDITS.md). **Wasserman's Filmmaker Suite, by Sam Wasserman and Wasserman Productions**, is built in: ScriptBreak, Cork Board, Master Canvas, Storyboard Reference Studio, Blockout, Motion Previs Studio, Slate, Circle Take, Stem Studio, Call Sheet and DaVinci MCP, each under its own licence (Apache-2.0; Master Canvas MIT). The Studio reads and writes their files. **Keys (drowzeys)** built the studio itself: the app, both builds, the TensorFold families and int8 kernels, and the Metal and CUDA engines it renders with. **Sam Wasserman and Wasserman Productions** helped design and build it in collaboration with Keys. Imagination Studio is by keys+Wasserman. **Thank you to everyone this stands on:** the Qwen team at Alibaba (Qwen-Image-2.1, Qwen3-VL), the MiniMax team (MiniMax H3), Ash Hart and the TensorFold contributors, antirez (h3.c), RobZombAI (H3MLX), mrbizarro (minimax-h3-mlx / Phosphene), Filip Strand and the mflux contributors, Viggle (the image turbo adapter up to 2.2), speach1sdef178 (the 2x video decoder), LightX2V (the video Turbo adapter), NVIDIA Research (Sol-Engine, Sol-Attn, Sol-H3), FastVideo (FastH3), and Apple's MLX team and every MLX contributor, Fish Audio (Drama 3, text to speech), the MiniMax team again (MiniMax-Music3, the Music tab's songs), Black Forest Labs (FLUX 3 Image Klein) and felix-ab (flux3-mlx), and OpenMOSS (MOSS-Image). This pack is their work, ported, pinned and measured. See [CREDITS.md](CREDITS.md). Built with Qwen. **Built with Fish Audio.** ## Which picture model to use: the evidence **The test:** a shootout on 2026-10-16 with Studio 2.8.1, run on a Mac Studio M5 Ultra and one DGX Spark. - **Pictures:** 8 prompts × 2 seeds × 4 models × 2 machines = **128 pictures at 1344x768, 0 failed jobs**. - **Judging:** by eye, from the full-size pictures. - **Full report:** every grid, the raw numbers and the method are in **[docs/picture-shootout.md](docs/picture-shootout.md)**. **Speed and memory**, per picture with the model loaded (median): | Model | Mac M5 Ultra | DGX Spark | Peak memory (Mac / Spark) | |---|---|---|---| | Qwen 2.1 Turbo, 8 steps | **4.7 s** | 8.6 s | 33 / 31 GB | | Qwen 2.1 Full, 40 steps at CFG 4 | 43.3 s | 80.1 s | 33 / 30 GB | | FLUX3-K Fast, 16 steps | 9.8 s | 10.5 s | 38 / 37 GB | | FLUX3-K Full, 50 steps | 59.9 s | 58.7 s | 40 / 37 GB | | FLUX3-K prompt upsampler, once per prompt | 5.2 s | 12.5 s | | - **Qwen:** runs about 2x faster a step on the M5 Ultra. - **FLUX3-K:** runs at the same speed on both machines. **Quality, the winner per prompt:** | Prompt | Winner | The evidence | |---|---|---| | Poster with title and tagline | Qwen 2.1 Full | Most coherent scene. All 32 posters, every model, spelled both lines exactly | | Photoreal portrait | Qwen 2.1 Full | Real skin and the whole face. FLUX3-K cropped off the mouth in 8 of 8 | | Character turnaround | Qwen 2.1 Full, narrowly | Gear consistent across views. FLUX3-K wins for a labelled line-art sheet | | Crowded night market | FLUX3-K Full | Lanterns, steam, mostly real words on signs. Qwen drew pseudo-script | | "Red cube on a blue sphere, left of a green pyramid" | FLUX3-K Fast | Right in **8 of 8**; Qwen 2 of 8 | | Food photography | FLUX3-K Fast | More professional styling. No model reliably drew the three pancakes asked for | | Watercolor | FLUX3-K Full | Truest washes and paper texture | | Hands at a piano | FLUX3-K Full | Five fingers on each hand in 8 of 8. Qwen drew a third hand 3 times | ![The spatial prompt: rows are Mac s101, Mac s102, Spark s101, Spark s102; columns Qwen Turbo, Qwen Full, FLUX3-K Fast, FLUX3-K Full](docs/shootout/5-spatial.jpg) *The spatial prompt. Every FLUX3-K picture (right two columns) has the pyramid on the right, as asked. Six of the eight Qwen pictures put it on the left.* **Conclusion: which to pick** - **Drafts and scouts:** use **Qwen 2.1 Turbo**. It is the quickest, and its quality is visibly below the other three. - **The everyday default:** use **FLUX3-K Fast**. It is right on layout and good on hands and crowded scenes, at about 10 s on either machine. - **Faces, portraits, posters and character sheets:** use **Qwen 2.1 Full**. It is twice as fast on a Mac. - **Final frames of complex scenes, illustration and hands:** use **FLUX3-K Full**. - **Machine:** for Qwen, the Mac when you have the choice. For FLUX3-K, the machine does not matter. **Limits:** two seeds a prompt and one judge, by eye. These model times leave out the text encode, so they are lower than the whole-run times in the table above. ### MOSS-Image on a DGX Spark **MOSS-Image** is OpenMOSS's 5B text-to-image model (Apache 2.0: commercial use is allowed). On a DGX Spark it is a fifth choice in the picture picker, for scouts, studio jobs and Nodes. It makes pictures from words only: it does not edit pictures. - **Install:** `bash oneshot-setup-spark.sh --moss` (30 GB: the SFT checkpoint and its Qwen3.5 9B text encoder in bf16, and the FLUX.2 small decoder, from Comfy-Org/MOSS-Image). It needs ComfyUI v0.42.0 or later, which this setup moves to. - **Settings:** ComfyUI's own template: 25 steps of er_sde at CFG 2.5, with its negative prompt. The prompt goes to the model as written. - **Mac:** `bash oneshot-setup.sh --moss` installs the same files (28 GB) on the Mac's own ComfyUI (the one of `--upscale` and `--remove`, PyTorch's Metal device, PyTorch's attention) and `scripts/image.sh` runs the Spark's graph there. Measured on 2026-10-20, 1344x768, seed 7, the same prompts on both machines: 71 s for the first picture with the model load and 59 s for the next on a Mac Studio M5 Ultra, 44 s each on a DGX Spark. The lettering came out exactly as written on both, and both are equally sharp; the compositions differ, because er_sde draws its noise during sampling on each machine's own device, so a seed does not give the same picture on a Mac and a Spark. Tested on one DGX Spark on 2026-10-19 (bf16, one seed per prompt, judged by eye): | | MOSS-Image | FLUX3-K Full | Qwen 2.1 Turbo | |---|---|---|---| | 1344x768, model loaded | 45 s | 69 s | 10 s | | 1024x576 (a scout picture) | 26 s | | | | Memory taken, from nothing loaded | 26 GB | | | | Poster: title, tagline and date | All three lines exact, cleanest layout | Title cut off at the top | Exact | - **Best at:** lettering. Posters, title cards, signs, menus with prices and labels came out exactly as written. A Japanese station sign was right too. - **Also good:** a photoreal portrait at dawn (warm, filmic, the light as asked) and anime key art. - **Not tested yet:** hands, the same character across pictures, and MOSS-Image's JSON layout prompts. - **Use it for:** posters, title cards, signage and anything with words in the frame, and pictures meant for commercial use. ![MOSS-Image, FLUX3-K Full and Qwen 2.1 Turbo on the same poster prompt](docs/shootout/moss-poster.jpg) ![The same film-still prompt: MOSS-Image at 25 and 50 steps, FLUX3-K Full, Qwen 2.1 Turbo](docs/shootout/moss-still.jpg) ## Speed: the first version on each machine, and 2.3 ![Benchmark: TensorFold against ComfyUI, h3.c, H3MLX, Phosphene and the Turbo LoRA, on a DGX Spark and a Mac Studio M5 Ultra](docs/benchmark/imagination-studio-benchmark.webp) *Every figure on the poster is a measurement from this README and the H3 shootout of [keys-Mac-TensorFold-MiniMax-H3-MLX](https://github.com/drowzeys/keys-Mac-TensorFold-MiniMax-H3-MLX). Most are one run each; the times include loading the model and decoding unless a row says per pass.* One run each unless a range is given; 5 second clips are 124 frames. Mac: Mac Studio M5 Ultra, 256 GB. Spark: one NVIDIA DGX Spark (GB10), models already loaded. | 5 second clip, 864x480 | Mac, first version (1.0, 2026-10-04) | Mac, 2.3 | DGX Spark, first build (ComfyUI's own blocks, bf16) | DGX Spark, 2.3 | |---|---|---|---|---| | What makes the video | MiniMax H3 + Turbo adapter on MLX, 3 passes | FastH3, 8 passes, TensorFold's Zig + Metal engine | FastH3, 8 passes, ComfyUI | FastH3, 8 passes, TensorFold's CUDA engine in int8 | | Image, then video from it | 74 s (103 s with the sound made again, the later default) | **79 s** (image 6 s, video 73 s) | 155 s | **115 s** (measured before the faster decoder below, which takes about 10 s more off) | | Text straight to video | not offered | **66 s** | 118 to 121 s | **73 s** | | One denoising pass | 9.5 s | **5.3 s** | 10.5 s | **7.1 s** | | Video decode | not timed apart | 13 s | 26 s | **14 s** | | Other sizes and lengths | Mac, first version | Mac, 2.3 | DGX Spark, first build | DGX Spark, 2.3 | |---|---|---|---|---| | 864x480, 4 passes, text to video | | 43 s | 71 s | 45 s (from the pass time) | | 1280x720, 5 s, text to video | | 199 to 217 s | not measured | **183 s** | | 1344x768, 5 s, text to video | | 175 s | 289 s | **about 195 s** | | 1344x768, 8 s, image then video | 364 s (709 s with the later default) | not re-measured | | | | 1280x720, 10 s (243 frames), text to video | | 378 s | not measured | 444 s | | One 15 s part of a chained 864x480 clip | | about 249 s | not measured | about 304 s | | One image, 1344x768 | 9.2 s (Viggle adapter, 6 steps) | **8.9 s** (Qwen-Image-2.1-Turbo, 8 steps; peak 15 GiB against 28) | 18 to 21 s with the model load | **9.5 s** | - The Mac's first version and 2.3 use different video models, so the 74 s and 79 s rows are not the same work: the first is 3 passes of MiniMax H3 with its Turbo adapter, whose sound was judged poor by ear; 2.3 is 8 passes of FastH3 with its own sound. Per pass the Mac went from 9.5 s to 5.3 s. - On the Spark the model is the same in both columns. The gain is the engine (10.5 s a pass to 7.1 s) and the 8-bit video decoder (26 s to 14 s). - Both engines are TensorFold H3 families on the TensorFold 1.0.4 base, from one commit of one branch. 1.0.3 and 1.0.4 are language-model releases, so the move changed nothing here: on each machine the same seed gives a bit-identical clip to the 1.0.2-based engine, at the same speed. ## Which Mac runs what Every row is Studio **2.3**. "Default" means images (Qwen-Image-2.1-Turbo) and FastH3 video with sound, the web app and the one-click app. Only the M5 Ultra with 256 GB has actually run it; every other row follows from memory measured on that machine and is marked untested. | Chip | Memory | What runs | Install command | Video engine it uses | Peak memory | Status | |---|---|---|---|---|---|---| | M5 family | 64 GB | Default | `bash oneshot-setup.sh --app` | FastH3, native Zig engine | 32 GiB | Untested | | M5 family | 96 GB | Default | `bash oneshot-setup.sh --app` | FastH3, native Zig engine | 32 GiB | Untested | | M5 family | 128 GB | Default, and MiniMax H3 Turbo if wanted | `bash oneshot-setup.sh --turbo --app` | FastH3 native; Turbo on the MLX engine with int8 kernels | 32 GiB; 103 GiB for Turbo | Untested | | M5 family | 256 GB or more | Everything | `bash oneshot-setup.sh --turbo --app` | The same | The same | **Tested** (M5 Ultra, 256 GB) | | M1 to M4 | 64 GB | Default, slowly | `bash oneshot-setup.sh --app` (add `Q8_ONLY=1` in front to save 65 GB of disk) | FastH3, MLX engine, 8-bit weights | 37 GiB at 480p, 40 GiB at 720p | Untested; emulated on the M5 | | M1 to M4 | 96 GB | Default, slowly | `bash oneshot-setup.sh --app` | FastH3, MLX engine, 8-bit weights | 37 to 40 GiB | Untested; emulated on the M5 | | M1 to M4 | 128 GB | Default, slowly. Turbo installs but is not recommended | `bash oneshot-setup.sh --app` | FastH3, MLX engine, 8-bit weights | 37 to 40 GiB | Untested; Turbo's memory without the int8 kernels is not measured | | M1 to M4 | 256 GB or more | Default, slowly; Turbo should fit | `bash oneshot-setup.sh --turbo --app` | FastH3 MLX 8-bit; Turbo on the MLX engine in bfloat16 | 37 to 40 GiB; Turbo not measured | Untested | | Any | 48 GB | Images only | `bash oneshot-setup.sh --image-only` | none | 28 GiB | Untested below 256 GB | How to configure it: - **Nothing to set on a normal install.** The setup reads the chip and the memory and picks the engine: the native engine on an M5, the 8-bit weights on an M1 to M4 (it converts them once, about 37 GB). It stops with a message if the Mac has too little memory for what was asked. - **Disk:** 175 GB for the default on an M5; 212 GB on an M1 to M4 (147 GB with `Q8_ONLY=1`); 64 GB more with `--turbo`; 33 GB for `--image-only`. - **Speed on an M1 to M4:** the 8-bit path took 31 s a pass at 864x480 on the M5 Ultra's GPU, against 5.3 s for the native engine there. An older GPU is slower again. Start with 480p, 5 seconds and 4 passes (`STEPS=4 bash scripts/fast.sh "a prompt" out.mp4`, or the 4 passes card in the app). - **Overrides:** `FASTH3_Q8=0` runs an M1 to M4 with the uncompressed weights (64 GiB peak: a 96 GB Mac or larger; no faster). `FASTH3_ENGINE=mlx` forces the MLX engine on an M5. `ENGINE=h3` asks for Turbo where it is installed. | Studio version | Smallest Mac for video | |---|---| | 2.0 and earlier | 128 GB (M5 for the fast path) | | 2.1 | 64 GB on an M5; 96 GB on an M1 to M4 | | 2.2 to 2.5 | 64 GB on any Apple silicon | On a **DGX Spark** the setup asks for 100 GB of memory for video (a Spark has 128 GB; about 70 GB was in use during a clip). No NVIDIA machine with 64 GB has been tried. Measured memory for each kind of job is under [Minimum requirements](#minimum-requirements). ## DGX Spark (CUDA) build The same Studio (web app, scripts, one-shot install) also runs on an **NVIDIA DGX Spark** (GB10, 128 GB unified memory, CUDA 13). FastH3's transformer runs there on **TensorFold's H3 family for CUDA**: the Mac engine's design (int8 projections, tile-routed int8 attention with FastH3's own sparse selection) on the GB10's tensor cores, written in Zig over the CUDA driver. **ComfyUI**, pinned, hosts the rest: the text encoder, the sampler, the decoders and Qwen-Image-2.1. The header of the web app says which build you are in: a green `DGX SPARK · NVIDIA GB10 · H3-CUDA` badge, or a purple `MAC · APPLE SILICON MX · H3-METAL`. On a GPU that header is `CUDA · · H3-CUDA`. **Thank you** to Ash Hart for TensorFold, FastVideo (Hao AI Lab) for FastH3 and its ComfyUI release, MiniMax for H3, the Qwen team for Qwen-Image-2.1-Turbo, and the ComfyUI team for the runtime this build stands on. ```bash git clone https://github.com/drowzeys/Imagination-Studio-by-keys-Wasserman.git && cd Imagination-Studio-by-keys-Wasserman sudo apt install -y git ffmpeg bash oneshot-setup.sh # the same command as on a Mac: on Linux it runs oneshot-setup-spark.sh # the core install (125 GB free disk): models, engines, motion graphics, Finish, # a test image and a 5 second test clip; --all for everything (about 400 GB) bash scripts/app.sh # http://127.0.0.1:7870 ``` ffmpeg is part of the install, and the installer stops without it. A Spark that was set up some other way (a copy of another Spark's folders, say) may lack it: the app then starts, says so in its log and in the page's header, and refuses the jobs that need it when they are queued. The 720p preset is not one of them any more: it generates 1280x736 and crops to 1280x720, and the crop is now made inside the ComfyUI workflow, before the clip is encoded (one encode instead of two); in 2.6.5 it was an ffmpeg pass after the render, and a Spark without ffmpeg lost the finished clip there. Without ffmpeg a clip also keeps ComfyUI's own sound track instead of the limited 256 kb/s one, and the render says so. #### Upgrade an existing install (DGX Spark) ```bash cd Imagination-Studio-by-keys-Wasserman && git pull bash oneshot-setup.sh --upgrade # on Linux: oneshot-setup-spark.sh --upgrade bash scripts/app.sh # restart the app on the new version ``` `--upgrade` moves an existing install to the version of the clone: the environment's packages, ComfyUI at its pinned commit, the engines (rebuilt or taken from the carrier only when their pinned commit changed), every optional part already installed (Ref2VA, FLUX3-K, MOSS-Image, Music, Voice, sound, Bolt, training), the Motion Previs environment, which it adds when it is missing, and motion graphics and Finish, which it adds to an install from before 3.2.0 (`--no-graphics`, `--no-finish` leave them out). Worker Sparks then take the head's code with **Sparks → Update and start**. It **fetches no weights** (a model that is not on disk is named and left out; an image-only install stays image-only), does not touch `~/ImaginationStudio` (clips, projects, LoRAs, settings), prints the version before and after, and ends with `--verify`. Running it a second time changes nothing. Restart the app afterwards (`bash scripts/app.sh`) so it serves the new pages. Run it when no clip is rendering. The installer takes the prebuilt engine from the carrier image when Docker is there, and otherwise builds it from source (`spark/build-engine.sh`: it fetches Zig 0.17.0 and the engine's source, [drowzeys/TensorFold `h3-1.0.4`](https://github.com/drowzeys/TensorFold/tree/h3-1.0.4), and needs the CUDA toolkit's `nvcc`, 12.9 or newer). Without it the Studio still works on ComfyUI's own blocks, slower. Measured on one DGX Spark (2026-10-09, one run each; 124 frames, text to video, models already loaded): | Size | Passes | TensorFold engine (int8) | A pass | ComfyUI's own blocks (bf16) | A pass | |---|---|---|---|---|---| | 864x480 | 8 | **73 s** | 7.1 s | 118 to 121 s | 10.5 s | | 864x480 | 4 | **45 s** (from the pass time) | | 71 s | | | 864x480 | 20 | **158 s** (from the pass time) | | 244 s (from the pass time) | | | 1280x720 (rendered 1280x736) | 8 | **183 s** | 18 s | not measured | | | 1344x768 | 8 | **about 195 s** | 20.5 s | 289 s | 29.4 s | The engine's column includes ComfyUI's 8-bit video decoder, the default here: it decodes 124 frames of 864x480 in 14 s where the fp16 one takes 26 s (29 s against 42 s at 1344x768), and the picture matches the fp16 decode to 42 to 44 dB. `SPARK_VAE=minimax_h3_video_vae_fp16.safetensors` selects the fp16 decoder if you download it. | Also | Time | |---|---| | A clip from an image, 864x480, 8 passes | 85 s | | First clip after a start, 864x480, 8 passes | about 115 s (ComfyUI's blocks: 144 to 156 s) | | Qwen-Image-2.1-Turbo image, 1344x768 | 9.5 s (20 s with the model load) | | Three scout images, 864x480 | 23 s | Lowest free system memory seen with the engine was 49 GB of 121 (35 GB on ComfyUI's blocks): the transformer is held in int8, 21 GB instead of 42. The first use converts the checkpoint on the GPU (about 90 s) and saves the int8 copy beside it (`….safetensors.tf-int8`, 21 GB of disk); later starts read that in a few seconds. How close the engine is: on the same input with dense attention, the stream after all 50 blocks has cosine 0.998 against ComfyUI's bf16 blocks. With sparse attention the same seed gives the same scene as ComfyUI's sparse path, and a repeat of a seed gives a bit-identical clip. What is different from the Mac build: - **Slower than an M5 Ultra**, about 1.35x a pass at 480p (7.2 s against 5.3 s). - **FastH3 only.** No MiniMax H3 Turbo, and no 2x decoder, so the sizes are 864x480, 1280x720 and 1344x768. - **Sparse attention at every size** (FastH3 was trained with it: a video tile keeps the best 20% of video tiles). `SPARK_SPARSITY=0` renders with dense attention, slower. - **Clips from an image want the app's prompt.** The web app writes MiniMax H3's first-frame lead line and structured prompt; a bare one-line prompt with `FIRST_FRAME` gave a jump cut in a test (on ComfyUI's own sparse path too). - `SPARK_ENGINE=comfy` renders on ComfyUI's own blocks in bf16 (dense attention under about 720p, its block-sparse kernel above). The 8-bit checkpoint through ComfyUI is no faster there (13.6 to 14 s a pass), and FastVideo's 4-bit Spark recipe is not used: on the test machine it gave flying debris and results that changed between runs. - **Why int8.** The GB10's tensor cores run int8 products at 245 TOPS and bf16 ones at 122 (measured on registers). PyTorch's stock int8 routine reaches about 52; the engine's own kernel 150 to 175 on FastH3's shapes. - ComfyUI keeps running with the models loaded after a render, and quitting the app stops it. `bash spark/comfy.sh stop` frees the memory while the app stays up. It runs at low priority with a memory guard beside it, because a Spark that runs out can become unreachable: see *The memory guard* below. ### More than one DGX Spark: the Sparks setting A job can use more than one DGX Spark. **Sparks**, a number from 1 to 8, is how many machines a job may use, the one the app runs on counted as one. It is at the top of the Queue on every tab, in the last step of Create, on the Production board and in a Slate shot; through the API it is `"sparks": 5` beside `kind` and `params`. The app shows how many Sparks are there and able and uses that many: choose 8 with five machines and it says *5 of 8 available* and uses five. - **Several clips** (jobs in the queue, a Production board's segments, a Slate scene's shots) render one clip per Spark, side by side. Each clip comes back into this Spark's Library, project or film at the path it would have had here, with its seed, its settings and the Spark that rendered it; the QA gate and the shot check run here. - **One clip** renders on one Spark. So do a long video (a chain of clips), pictures, edits and Library actions, on this Spark; the page says which and why before the job is queued. - **The Sparks tab** is the list of the other Sparks: add one by the address you would type after `ssh`, and **Update and start** sends it this Spark's Studio code (rsync, as you; models are not sent) and starts its app. Each row shows whether the Spark answers, its Studio's version, memory and disk free, what it can render (FastH3 clips, Ref2VA, the image model, LoRAs, ffmpeg) and whether it is busy. It needs a key login to each Spark and the Studio installed there once; nothing runs as root. - **From a Mac** the Studio leads the Sparks the same way. A Spark is given the single clips it renders with its own settings (FastH3 at the sizes made without the 2x decoder); MiniMax H3 Turbo qualities, the 2x decoder presets and a text encoder file of the Mac's stay on the Mac, and the page says why. A Mac that reaches only the Spark that leads the others gives it as **Through**: that Spark logs in to each worker with its own key, so no key is added to a worker. One-time setup and the measured run: [A Mac at the head](docs/multi-spark.md#a-mac-at-the-head). - **Safe by construction.** A worker's app listens only on its own `127.0.0.1`; this app reaches it through an ssh tunnel it opens and closes itself. One job runs on a Spark at a time. Every clip passes the worker's own memory guard before it is sent, and a clip with a LoRA goes only to a Spark that has the LoRA and whose guard accepts it. A worker that stops answering for 60 seconds is marked down and its clip is rendered elsewhere; cancel reaches the worker. Measured through the app's own API on 2026-10-13, eight 56-frame 864x480 FastH3 clips, models loaded, one run each: | Sparks | 1 | 2 | 3 | 4 | 5 | |---|---|---|---|---|---| | Wall time, 8 clips | 263 s | 130 s | 98 s | 69 s | 65 s | | Against one Spark | 1.00x | 2.03x | 2.70x | 3.81x | 4.05x | Five were barely better than four for eight clips (two rounds of clips either way); the fifth pays from ten clips. Six to eight are not measured: five machines exist here. One clip split across Sparks is measured outside the app (1.40x, 1.73x and 1.77x on two, three and four) and is not built in. The whole of it, with what is not built: [docs/multi-spark.md](docs/multi-spark.md). ### The memory guard (DGX Spark) On a Spark the GPU and the system share one pool of memory. On 2026-10-13 one clip with a LoRA at 1376x768 took a worker Spark down: the app accepted it, and the server's check (once a second, against 4 GB) did not act. The guard in `spark/memory_guard.py` replaces that check, in three layers that every way of starting a render goes through (the app, `scripts/video.sh`, `spark_generate.py`, a worker's copy of this folder): 1. **Before a clip is queued, and again before anything is loaded**, its peak is estimated from measured peaks. A clip that would leave the machine under its floor is refused, with the reason and the nearest size and length that fit. In the app the settings step shows the same line before you queue; a clip that fits shows "about N GB of this machine's memory". 2. **While the server runs**, the guard reads the machine's memory twenty times a second. It stops the server when available memory is under the floor (a tenth of the machine: 12.4 GB; `SPARK_MEMORY_FLOOR_MB`), or is falling so fast that it would be under a third of the floor within a second, or when the kernel reports work stalled on memory. The job fails with that reason. It also gives the model files' page cache back when little memory is free (`SPARK_MEMORY_FREE_MB`, 8.3 GB): the GPU driver takes its memory from what is free, not from what is available. 3. The server is marked as the first process the kernel ends if memory runs out all the same. | Measured on a 124.5 GB Spark, nothing else running (118.7 GB available) | Rows a pass | Taken at the lowest point | |---|---|---| | TensorFold int8 engine, 864x480, 124 frames | 15.4K | 57.6 GB | | TensorFold int8 engine, 1376x768, 124 frames | 39.2K | 62.2 GB (62.1 to 64.2 in a production run) | | TensorFold int8 engine, 1376x768, 192 and 243 frames (that run, sampled every 11 s) | 59.9K, 75.3K | 67.1 GB, 69.0 to 70.9 GB | | With a LoRA merged into the int8 engine (2.7.1), 864x480, 124 frames | 15.4K | 57.6 GB | | With a LoRA merged into the int8 engine, 1280x736 and 1376x768, 124 frames | 35.0K, 39.2K | 61.4 GB, 62.3 GB | | With a LoRA merged into the int8 engine, 1376x768, 192 and 243 frames | 59.9K, 75.3K | 66.5 GB, 69.7 GB | | With a LoRA on ComfyUI's own blocks (bfloat16; before 2.7.1), 864x480, 56 and 124 frames | 7.3K, 15.4K | 75.4 GB, 75.9 GB | | The same, 1280x736, 22, 56, 90, 107 and 124 frames | 7.4K to 35.0K | 74.8, 75.8 to 76.2, 76.2, 76.5, 76.6 GB | The estimate is a straight line through those points for each route plus 6 GB (no point is more than 1.7 GB from its line; most are one run). Three things it does not know, said plainly: - **A clip with a LoRA is refused above the largest one measured on its route.** Since 2.7.1 a LoRA on FastH3 is merged into TensorFold's int8 engine and takes what a clip without one takes (the table: within 0.6 GB at every size), measured from 864x480 up to 1376x768 at 243 frames (75.3K rows), as far as the engine itself was measured: every size the app offers, up to 10 s at the Sharp Spec size. On ComfyUI's own bfloat16 blocks (a Spark whose engine predates 2.7.1, or `--engine comfy`) it stays refused above 1280x736 at 124 frames (35.0K rows), whatever the line says. By that line the clip that took the Spark down needs 77 GB and leaves 41, and that is what the machine showed for a minute before it was lost, so its end is not explained by how much memory the clip takes and the size is not offered there. Its logs were never read; what is known is in the next paragraph. - Reference clips (Ref2VA), MiniMax H3's own transformer and a Spark without the engine were not measured here; they are estimated on the bfloat16 LoRA line with their references' rows added. - Pictures, edits and Library actions are not estimated: the running guard covers them. What was found about the old check: memory falls far faster than once a second can follow (7.2 GB a second while ComfyUI loads a model from the page cache, 13 GB in half a second while TensorFold's engine loads its weights), and "available" counts the page cache. With a LoRA, ComfyUI loads until nothing is free: free memory sat at 1.2 to 2.5 GB through every such render while 43 GB read as available, and the kernel logged the GPU driver being refused memory (`NVRM ... NV_ERR_NO_MEMORY`) at 2 GB free and 54 GB available. With the guard beside the server the same renders kept 8 GB or more free, took the same time (146 s against 145 s), and the driver logged nothing. Proof of the two refusals, without a machine near its limit: the app and `scripts/video.sh` both refuse the lost clip before anything loads, and a server started with `SPARK_MEMORY_FLOOR_MB=70000` was stopped during a small clip's load at 69.8 GB available (lowest point 69.77 GB while memory fell at 28 GB a second; gone in 0.5 s, the job failed with the reason). `python3 spark/memory_guard.py check --width 1376 --height 768 --frames 124 --lora` prints a verdict. **Refusals on an idle Spark with a newer NVIDIA driver (590 and later).** These drivers keep the memory a CUDA process frees in their own system memory pools after it ends, and the kernel counts it nowhere: not as available, not as cache, not as any program's. ComfyUI stops before some renders, so the next clip can be refused for memory that is not in use, often with a whole batch failing within seconds (issue #59). `/proc/driver/nvidia/params` shows `EnableSystemMemoryPools` set to a non-zero value. When a refusal finds much of the machine held outside every program and no GPU program running, it says so. Two fixes: - Install the driver NVIDIA validates for the DGX Spark, the R580 branch (the DGX Dashboard's update, or DGX OS's own packages). The studio's Sparks run 580.173.02, which keeps no such pools. - Or turn the pools off: `echo "options nvidia NVreg_EnableSystemMemoryPools=0" | sudo tee /etc/modprobe.d/nvidia-no-sysmem-pools.conf`, then `sudo update-initramfs -u` and restart the Spark. Memory comes back within a second of a process ending. Leave `nvidia-persistenced` running: on a Spark without a display, stopping it broke GPU start-up (`RmInitAdapter failed`). `sudo sysctl vm.drop_caches=2` gives the pooled memory back once, without a restart. Longer clips on the engine: 10 seconds at 1280x720 (243 frames) took 444 s, a pass 43.7 s, against 378 s on an M5 Ultra for the same prompt; a 30 second chained clip at 864x480 (two 15 second parts) took 611 s. Not tested: clips longer than 15 seconds in one piece, chains longer than 30 seconds, a download of the models from nothing (the files were already on the test machine), any GPU other than the GB10, and two Sparks working on one clip. ### A Spark shared with other work: the workload governor (optional) A Spark that also runs other model servers can have a machine-wide workload governor: a small service that hands out memory reservations through an admission socket, so two heavy jobs never start on the same memory. Point the studio at its folder with `SPARK_GOVERNOR` and the studio takes part (`spark/workload.py`); leave it unset and nothing changes. - **The renderer starts inside a reservation of its own** (`spark/managed_comfy.py`, used by `spark/comfy.sh`). It first releases its own reservations whose servers have ended, waits for the governor to allow the start, then starts ComfyUI with the same memory guard beside it and the governor's telemetry as well: memory under the floor, a GPU at 90 C or throttling, a platform zone near its trip point or a minute of memory pressure stop the server, and the render says why. `bash spark/comfy.sh stop` ends that server only (each port has its own `$PREFIX/managed-comfy-.json`). - **Every job waits for a cool, calm start** (`spark/start_gate.py`, before each submission): the server must still hold its reservation, the GPU must be under 80 C and memory not under pressure. It waits up to three minutes, then the job fails with the reasons. - **Drama 3 too**, with `DRAMA3_MANAGED_GUARD=1` (`spark/managed_voice.py`): the engine starts in its own reservation, after the studio's renderer is stopped when idle (never while it renders), keeps its own 8 GB kill-guard, and each part of a take waits for the same start checks. `SPARK_GOVERNOR_MIN_FREE_GIB` (12, never lower), `SPARK_GOVERNOR_RESERVE_GIB` (85 for a render), `SPARK_GOVERNOR_VOICE_GIB` (55), `SPARK_GOVERNOR_OWNER` and `SPARK_GOVERNOR_VOICE_OWNER` (the names it reserves under), `SPARK_GOVERNOR_SOCKET`, `SPARK_GOVERNOR_FREE_URL` (another ComfyUI on the machine whose idle model cache is freed before a reservation; a busy one is never touched) and `SPARK_THERMAL_ZONES` adjust it. `spark/workload.py` says what the governor folder must hold. `DELIVERY_AUDIO_RATE=48000` (also 44100 or 96000) delivers a Spark clip's sound as stereo AAC at that rate for an editor's timeline; ComfyUI's H3 decoder makes 32 kHz. The picture is copied unchanged, and resampling adds no detail. ## What changed > **2.9.3 (2026-10-17): FLUX3-K's new file names.** BFL renamed the Klein checkpoints; the Spark setup fetches the > new names and renames old ones. Details: [New in 2.9.3](#new-in-293). > **2.9.2 (2026-10-17): Music on TensorFold's native Metal engine on an M5 Mac.** No live playback there yet; an M1 to > M4, or `MUSIC3_BACKEND=mlx`, keeps the MLX port and its live playback. Details: [New in 2.9.2](#new-in-292). > **2.9.1 (2026-10-16): Music on both machines.** int8 weights and play while it's made on a Mac; TensorFold's CUDA > engine for MiniMax-Music3 on a DGX Spark. Details: [New in 2.9.1](#new-in-291). > **2.9.0 (2026-10-16): Music, MiniMax Music 3.** A Music tab: a song from lyrics and a style, laid under a video if > you like. Details: [New in 2.9.0](#new-in-290). > **2.8.1 (2026-10-16): Qwen 2.1 Full.** The picture-model choice adds Qwen-Image-2.1's 40-step base model as > **Qwen 2.1 Full**, beside **Qwen 2.1 Turbo**, as FLUX3-K has Fast and Full. > - **Guidance:** it runs with true classifier-free guidance at 4 and an empty negative prompt, as Qwen's own pipeline > does: ComfyUI's CFG-4 workflow on a DGX Spark, and `qwen_image_generate.py --cfg 4` on a Mac. The Mac's > generator had no guidance before. > - **Where it is offered:** where the base weights are installed (`--image-edit`). > - **Measured on an M5 Ultra at 1344x768:** Turbo 12 s, Full 47 s, both peaking at 15.3 GiB. Full without guidance > took 24 s and looked barely better than Turbo. > - **Docs:** the README opens with what is new, the 2.8.0 demo and a new banner. > **2.8.0 (2026-10-15): pick the picture model: Qwen 2.1 Turbo, FLUX3-K Fast or FLUX3-K Full.** Black Forest Labs' > FLUX 3 Image Klein ("FLUX3-K") joins Qwen-Image-2.1-Turbo wherever the Studio makes a picture from a prompt: *Image > only*, the scouts a clip starts from, and a last frame. **FLUX3-K Fast** is the distilled checkpoint (16 steps), > **FLUX3-K Full** the base one (50 steps at CFG 3.5); both get a full scene caption from BFL's prompt upsampler first. > On a DGX Spark they run on ComfyUI's own FLUX 3 nodes (ComfyUI moves to `5a665890`, v0.41.0 and later), upsampler > included, so the render stays under the memory guard; on a Mac on flux3-mlx (Apache-2.0, a port of BFL's reference > code) in an environment of its own. `oneshot-setup.sh --flux3` installs them (41 GB; the Hugging Face repos are gated: > accept the FLUX Kommunity License first). Measured at 1344x768 with the model loaded, bf16: Qwen Turbo 9.7 s on an > M5 Ultra; FLUX3-K Fast 10.3 to 10.7 s on a Spark (ComfyUI; diffusers took 15.5 s for the same picture) and 14.9 s on > an M5 Ultra; FLUX3-K Full 57.5 s on a Spark and 53.6 s on an M5 Ultra; the upsampler, once per prompt, about 18 s on > a Spark and 5 s on an M5 Ultra. Against Qwen on the same six prompts and two seeds, FLUX3-K spelled a poster's title and tagline > right where Qwen wrote "SOME ONE IS STOLL LISTENNING", drew whole figures for character sheets where Qwen cut them at > the waist, and kept two characters recognisable in a two-reference shot; Fast looked as good as Full. The same seed > draws a different picture on the two machines (the runtimes draw their noise differently). **Edit a picture** offers > the same choice: FLUX3-K takes the picture, and up to nine more beside it, as reference latents (BFL's edit layout: > every picture on the prompt and on the negative, the result at the first picture's size), and the upsampler sees the > pictures before it writes the caption of the result (its i2i system prompt). Image LoRAs, the camera ring and the > 40-step base model stay Qwen's. Measured with the job's own scripts, seed 7, 1344x768, upsampler included: an edit > took 24 s with Qwen, 37 s with FLUX3-K Fast and 139 s with Full on a DGX Spark, 10, 37 and 240 s on an M5 Ultra; two > pictures (a person from one put in the place of another) 23, 44 and 188 s on a Spark, 13, 42 and 427 s on the Mac. > The reference rows lengthen every step, so Full is slow for edits on a Mac (about four times its text-to-image > time): Fast is the FLUX3-K for edits. FLUX3-K follows "switch the neon off" where Qwen left the signs lit on the > Spark. FLUX 3 Image Klein is under the FLUX Kommunity License: non-commercial use, and commercial use of the > outputs only for Qualifying Users (under US$5M revenue). The carrier image moves to > `ghcr.io/drowzeys/keys-tensorfold-studio:2.8.0`: the same Mac payload, and the DGX Spark's prebuilt engine is now the > LoRA build of 2.7.1 (`h3-cuda-lora` `1f7ecc75`), so a Spark install takes it instead of building it from source. > **2.7.8 (2026-10-15): no dropped lines on the Spark, a cloned voice at once on the Mac.** On a DGX Spark the app > now gives every take the Mac engine's end-of-text check (`app/wordcheck.py`, its rules copied from TensorFold's > `drama3-mlx`): Whisper hears the take, and when words are missing (under 85 % heard, three in a row, or the ending) > the take is made again with another seed, not played, twice at most, the best kept and the first one's check kept > beside it. The two-speaker example on seeds 1 to 8 through the head app: before, seed 2 lost its last line (15 of > 20 words) and the other seven were whole; after, all eight whole, seed 2 drawn again once (42.7 s for the take > against 18 to 25 s for the others). On the Mac a voice to clone left without its words is heard by the engine's own > Whisper on the GPU (TensorFold `drama3-mlx` 8f3d4b33), not by the app's on the CPU first: the first sound of a > cloned take reaches the page **0.56 to 0.63 s** after the request, against 10.6 to 10.8 s in 2.7.7. > **Built with Fish Audio.** > **2.7.7 (2026-10-15): Voice answers at once on the Mac, and dialogue keeps its lines.** The Mac's Drama 3 now runs > as a persistent server, like the Spark's (TensorFold `drama3-mlx` 92c09a8e, `--serve`): the first take loads it > (5.2 s, about 50 GB with its word check), later takes start at once, and it is unloaded after five idle minutes and > before any video or picture job, holding the studio's GPU lock while loaded. **Once loaded, a take's first sound > reaches the page 0.23 to 0.30 s after it is asked for** (5.4 s on the first take, 2.7.6's every take). A two-speaker > line is now sent a turn a line on both machines (on one line Drama 3 dropped later turns; Fish's own prompt format), > and the tab's example is written so: on the M5 Ultra the two-speaker example came back whole on seeds 1 to 4, three > of them while playing and one made again without playing. The Mac engine also redraws a part whose last word is > missing. Measured through the Mac app: a 46 s read in 16.8 s, a Slate cast line in 1.5 s, every take's words all > heard. **Built with Fish Audio.** > **2.7.6 (2026-10-15): the Mac's Voice tab catches up with the Spark's.** On a Mac a take now **plays while it is > made** (the engine's `--stream`, through the same `/api/voice/takes/{id}/live` as on a Spark) and the engine > **checks its own words** as it goes (TensorFold `drama3-mlx` 4a8a982f: Whisper on MLX after each part, a runaway tail > cut, a part with missing words drawn again, and a stop rule on the model's own end signal). The check's word count > is the take's score, so the take is no longer transcribed a second time, and the tab shows it part by part, with > the time to the first sound. A part cannot be drawn again once it has been heard: a take that comes out short while > playing is made once more without playing (*Play while it is made* off does that from the start). Measured on the > M5 Ultra through the app: 8 s of speech in 8.7 s from the click, the first sound 5.3 s after it (start-up and the > 3.7 s load, then 0.22 s), a 46 s read in 21.9 s. **Both engines are now public** on > [drowzeys/TensorFold](https://github.com/drowzeys/TensorFold) (`drama3-mlx` for the Mac, `drama3-cuda` for the > Spark), and `oneshot-setup.sh --voice` installs the Mac one (`scripts/setup-voice.sh`); on a Spark it checks the > hand-built engine and prints the steps. **Built with Fish Audio.** > **2.7.5 (2026-10-15): Voice on the DGX Spark.** The Voice tab now works on a DGX Spark as on the Mac, through > TensorFold's native CUDA port of Fish Audio Drama 3 (`drama3-generate`, Zig + CUDA, no Python at run time; nothing is > sent to Fish Audio). The engine is a persistent server: the first take loads it (about 18 s, 41.6 GB at the peak), > later takes start at once, and it is unloaded, its memory given back, after five idle minutes and at once before any > video or picture job of the queue starts, so it never stands beside MiniMax H3. It holds the studio's GPU lock for as > long as it is loaded and stands under its own memory guard. A take **plays while it is made** (the first sound > about 0.3 s after the take starts on a loaded engine). Long text goes in parts and keeps one voice. Measured on a > GB10 through the app: 28 to 31 frames a second (a take made in 0.7 to 0.8 times its length), Whisper 92 to 100 % > on a plain line, a cloned voice, two speakers, a Slate cast voice and a 56 s read. See > [Voice on a DGX Spark](#voice-on-a-dgx-spark-cuda). **Built with Fish Audio.** > **2.7.4 (2026-10-15): Voice, with Fish Audio Drama 3.** A new **Voice** tab speaks text with Fish Audio Drama 3 on > the Mac itself, through TensorFold's MLX port (nothing is sent to Fish Audio): up to 16 speakers in one take > (`<|speaker:0|>` ... `<|speaker:15|>`), `[directions]` for the delivery (`[whispering]`, `[sighs]`, or a description of > the voice), and a voice cloned from a clip, with a consent box that must be ticked. Each take runs in the render > queue, one at a time and never beside a video (Drama 3 peaks at about 50 GB), under the studio's GPU lock; Whisper > then checks its words. *Use as Ref2VA voice reference* puts a take into the create wizard as reference audio > (`