Enterprise Open-Source AI Video, Movie & Direct-Response Ad Engine. Turn any story concept, viral premise, or product URL into finished multi-scene video productions.
Darc Studio is a local-first alternative to Runway, Sora, Arcads, Creatify, and Higgsfield. It generates cinematic quick movies, viral social scenes & micro-dramas, POV suspense shorts, and high-converting direct-response video ads powered by the full roster of state-of-the-art video models including ByteDance Seedance 2.5/2.0, Kling 2.0 Pro, Wan 2.2 Turbo, Minimax Hailuo-02, Luma Ray 2, and Runway Gen-3 Alpha.
Zero monthly subscriptions. Zero per-video credits. 100% local-first and privacy-preserving.
# 1. Generate a cinematic micro-drama with ByteDance Seedance 2.5
python3 main.py --mode movie --premise "A lone astronaut discovers an ancient glowing doorway on Europa" --video-model seedance-2.5 --style cyberpunk
# 2. Or generate high-converting e-commerce video ads from any product URL
python3 main.py --url https://your-store.com/products/your-product --variants 3
# 3. Launch the visual Web Studio
python3 app.py- Why this exists
- Creation Modes & Capabilities
- Supported Video Models (Seedance, Kling, Wan, Minimax, Luma)
- Install
- Quick start
- Zero-key offline mode
- The Web Studio
- CLI reference
- Storytelling & Ad Frameworks
- UGC avatars
- Brand kit
- Configuration
- Testing
- Licence
Commercial AI video & ad platforms charge $40–500/month and cap resolution, generation time, camera control, and script lengths. Darc Studio gives you direct access to the world's best models (Seedance 2.5, Kling 2.0, Wan 2.2, ElevenLabs, Whisper, FFmpeg) with unified prompt engineering, professional camera rig motion, and word-level karaoke subtitles.
Everything runs on your machine. Your data and renders stay 100% private.
| Creation Mode | Description |
|---|---|
| 🎬 Quick Movies & Cinema | Multi-scene cinematic storytelling with plot escalation, character dialogue, Hollywood camera physics (Dolly, Drone Orbit, FPV Flythrough, Whip Pan), and film-grade visual grading. |
| 📱 Viral POV & Social Shorts | High-retention social scenes, Reddit confessions, suspense thrillers, and comedy skits with word-level glowing karaoke subtitles. |
| 🛍️ Direct-Response Video Ads | Automated storefront scraping, 7 conversion frameworks (PAS, UGC Testimonial, 3 Reasons, Unboxing), UGC avatar presenters, and branded end cards. |
| 🎨 Multi-Model Canvas | Side-by-side model comparison across ByteDance Seedance 2.5, Kling 2.0, Wan 2.2, Minimax Hailuo-02, Luma Ray 2, and Runway Gen-3. |
| Real product photography | Scrapes and normalizes the actual images from your store, so the product on screen is the product you sell |
| Direct-response scripts | Seven proven hook frameworks (PAS, UGC testimonial, listicle, before/after, us-vs-them, myth-bust, unboxing) |
| A/B variants in one run | --variants 3 writes three genuinely different angles, not three rewordings |
| Word-level captions | TikTok-style karaoke captions with the spoken word highlighted, aligned to the real audio |
| Ken Burns motion | Every still gets a camera move, varied per scene, so nothing sits static |
| Crossfade transitions | Frame-accurate: the finished runtime always equals the narration length |
| Music that behaves | Loops to length, side-chain ducks under the voiceover, normalized to −14 LUFS |
| UGC presenters | Lip-synced talking-head actors delivering your script |
| Brand kit | Logo watermark, accent colour, caption styling, CTA end card |
| Multi-platform | 9:16 vertical, 1:1 square, 16:9 landscape |
| Script auditing | Warns when a hook runs long, a CTA is missing, or copy drifts into ad-speak |
DarcAds needs Python 3.9+ and ffmpeg.
# macOS
brew install ffmpeg
# Debian / Ubuntu
sudo apt update && sudo apt install ffmpeg
# Fedora
sudo dnf install ffmpeg
# Windows
winget install Gyan.FFmpegDarcAds does not require ffmpeg to be built with libass or libfreetype — all text is rendered with Pillow, so a stock build is fine.
git clone https://github.com/<your-username>/DarcAds.git
cd DarcAds
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtpython3 main.py --doctorDarcAds environment check
ok ffmpeg + ffprobe
- Anthropic (script engine)
- OpenAI (script engine + TTS)
...
DarcAds runs fully offline with no keys - quality simply improves as you add them.
A row of - is fine. Only the ffmpeg line has to say ok.
# The simplest possible run
python3 main.py --url https://your-store.com/products/your-product
# A 30-second ad with three A/B variants, music and a brand kit
python3 main.py \
--url https://your-store.com/products/your-product \
--variants 3 \
--seconds 30 \
--music assets/music/upbeat.mp3 \
--brand-name "Acme" \
--accent "#FF4D6D" \
--cta "Get 20% off"
# Browser UI instead
python3 app.py # opens http://localhost:7860Output lands in output/, alongside a *_run.json manifest recording exactly
what was scraped, written, generated and rendered.
This is the important part. With nothing configured, DarcAds still:
- scrapes your real product images, price, features and reviews
- writes a complete 5-scene script using a built-in framework director
- generates a voiceover with free TTS
- composes each product photo into the frame without cropping the product away
- animates every scene with a Ken Burns move
- burns in word-level captions, hook text and a progress bar
- crossfades, ducks music, normalizes loudness and renders a branded end card
Here is a real frame from a keyless run — no LLM, no video model, no paid voice:
┌────────────────────┐
│ 3 REASONS │ ← hook text, brand accent colour
│ │
│ [product] │ ← your real photo, whole product visible
│ │
│ replaced │ ← karaoke captions, spoken word highlighted
│ everything else. │
│ ▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱ │ ← progress bar
└────────────────────┘
Adding keys improves individual stages. It is never required to get a video.
DarcAds detects what is installed and configured, and upgrades that stage
automatically. Copy .env.example to .env and fill in only what you want.
cp .env.example .env
pip install -r requirements-optional.txt # or install individual packages| Stage | Free default | Upgrade | How |
|---|---|---|---|
| Script | Built-in framework director | Claude / GPT / local Llama | ANTHROPIC_API_KEY, OPENAI_API_KEY, or run Ollama |
| Voice | gTTS | edge-tts (free, much better) | pip install edge-tts |
| Kokoro-82M (free, local) | pip install kokoro |
||
| ElevenLabs (best) | ELEVENLABS_API_KEY |
||
| Video | Product photos + Ken Burns | Wan 2.1 / Kling / Hunyuan / Luma / MiniMax | FAL_KEY |
| Your own GPU | run ComfyUI, set COMFYUI_WORKFLOW |
||
| Captions | Estimated timings | True word alignment | pip install faster-whisper |
| Avatars | — | Lip-synced UGC presenters | FAL_KEY + an actor asset |
Best value for one command:
pip install edge-tts. It replaces the robotic default voice with Microsoft neural voices, costs nothing, and needs no API key.
Local LLMs are auto-detected — if Ollama is running on its default port, DarcAds will use it with no configuration:
ollama serve
ollama pull llama3.1
python3 main.py --url <product-url> # picks up Ollama automaticallypython3 app.pyOpens a studio at http://localhost:7860 with everything the CLI exposes:
variants, hook framework, platform, quality, voice, caption style, transitions,
UGC presenter, brand kit, music and logo upload, plus optional session-only API
key fields.
Keys typed into the UI apply to that process only and are never written to disk.
python3 main.py --url <product-url> [options]
Core
| Flag | Default | Description |
|---|---|---|
--url |
required | Product page URL |
--output |
output/final_ad.mp4 |
Output path |
--variants |
1 |
Number of creative variants (each uses a different framework) |
--seconds |
20 |
Target runtime |
--framework |
pas |
pas, testimonial, three_reasons, before_after, us_vs_them, myth_bust, unboxing |
--doctor |
Report configured engines and exit |
Script engine
| Flag | Default | Description |
|---|---|---|
--provider |
auto |
auto, anthropic, openai, openai_compatible, offline |
--model |
provider default | Model name override |
--base-url |
Custom LLM endpoint (Ollama, LM Studio, vLLM) |
Audio
| Flag | Default | Description |
|---|---|---|
--voice |
female_energetic |
female_energetic, female_calm, male_confident, male_casual, neutral |
--tts |
auto |
Force an engine: elevenlabs, openai, kokoro, edge, gtts |
--music |
Background track | |
--music-volume |
0.18 |
Music level, 0–1 |
--no-duck |
Disable side-chain ducking |
Visuals
| Flag | Default | Description |
|---|---|---|
--platform |
tiktok |
tiktok, reels, shorts, vertical, square, landscape |
--quality |
standard |
draft (fast), standard, high |
--captions |
karaoke |
karaoke, word_pop, block, none |
--transition |
fade |
none, fade, dissolve, slideleft, slideright, wipeleft, circleopen, smoothleft |
--video-model |
wan-2.1 |
wan-2.1, wan-2.2, kling, kling-standard, hunyuan, luma, minimax, ltx |
--no-generation |
Never call a paid video model — stay free | |
--no-whisper |
Skip Whisper alignment, estimate caption timings |
UGC avatar
| Flag | Description |
|---|---|
--avatar |
Use a lip-synced presenter for every scene |
--actor |
Actor name, file path or URL |
--avatar-model |
auto, hedra, sync-lipsync, latentsync, veed |
--list-actors |
List available actors and exit |
Brand kit
| Flag | Default | Description |
|---|---|---|
--brand-name |
scraped vendor | Name on the end card |
--logo |
Transparent PNG watermark | |
--accent |
#00E5A0 |
Highlight colour for captions and CTA |
--text-color |
#FFFFFF |
Base caption colour |
--cta |
Shop now |
End card call to action |
--cta-seconds |
1.6 |
End card length (0 disables) |
--font |
auto | Font family name or .ttf path |
The Arcads-style feature: a person on camera delivering your script.
- Put a portrait photo or a short clip of a presenter in
assets/actors/ - Add a
FAL_KEYto.env - Run with
--avatar
python3 main.py --url <product-url> --avatar --actor mayaA still portrait is animated to speak the narration (Hedra Character-2). A video clip is re-synced to it (sync-lipsync). DarcAds picks the right mode from the file type.
Only use likenesses you have the rights to, and disclose synthetic media where your market requires it. See
assets/actors/README.md.
python3 main.py --url <product-url> \
--brand-name "Acme" \
--logo assets/brand/logo.png \
--accent "#FF4D6D" \
--text-color "#FFFFFF" \
--cta "Get 20% off" \
--cta-seconds 2.0 \
--font assets/fonts/Montserrat-ExtraBold.ttfThe accent colour drives the highlighted caption word, the hook text, the progress bar and the CTA button, so one value re-skins the whole ad.
Product URL
│
▼
1. INGEST src/ingest.py
Shopify JSON → JSON-LD → OpenGraph → <img> sweep
Extracts title, price, features, reviews, ratings, high-res images
│
▼
2. DIRECT src/llm_director.py
Framework + product data → LLM → validated scene timeline
Hallucinated image references are snapped back to real URLs
│
▼
3. GENERATE src/voice.py · src/visuals.py · src/avatars.py
Narration first (its duration times everything downstream)
Then visuals: product photo → animated photo → generated clip → designed frame
│
▼
4. ALIGN src/captions.py
Word timings (Whisper or estimated) → one transparent overlay layer
containing captions, hook text, logo and progress bar
│
▼
5. COMPILE src/compiler.py
Scene clips (Ken Burns) → crossfade spine → audio bed (duck + loudnorm)
→ composite overlay → final MP4
│
▼
output/final_ad.mp4 + run.json
Three design decisions worth knowing:
Narration drives all timing. Each scene clip is cut to the exact length of
its voiceover. Clips are rendered transition seconds longer than their
narration (all but the last), so each crossfade consumes exactly that overlap
and the finished video lands on the exact sum of the narration durations.
Captions therefore stay frame-accurate against the audio.
Text is drawn with Pillow, not ffmpeg. Many ffmpeg builds ship without libass or libfreetype. Rendering captions and overlays with Pillow means DarcAds behaves identically on every machine, and gives pixel-level control over the caption pop. The whole burned-in layer is emitted as a PNG sequence plus a concat playlist, so it costs exactly one extra ffmpeg input.
Every stage degrades instead of failing. No API key, a dead image URL, a provider outage or a missing optional package all fall back to the next-best option and log what happened. A run that cannot reach a single external service still produces a finished video.
These are encoded in the director prompt and enforced by audit_timeline():
- The hook is under 9 words and never opens with the brand name, "Introducing", or a question the viewer would answer "no" to
- One idea per scene, roughly one spoken breath
- Second person, contractions, short sentences — written like a person, not a brand
- On-screen text never duplicates the narration — captions already carry the words, so the burned-in headline reinforces instead of repeating
- No invented claims — specs, prices, guarantees and reviews come from the scraped data or are omitted
- Banned ad-speak: "elevate", "game-changer", "revolutionary", "seamless", "unlock the", "look no further"
- The last scene always has a call to action with a reason to act now
Warnings are printed after every run and stored in the run manifest:
Script notes for 'testimonial':
- Hook is 13 words; under 9 keeps the scroll-stop.
Beyond the CLI, src/config.py exposes platform and quality presets:
from src.config import build_render_config
from src.models import BrandKit
config = build_render_config(
platform="tiktok", # frame size and fps
quality="high", # x264 preset, CRF, bitrate, motion intensity
brand=BrandKit(name="Acme", accent_color="#FF4D6D"),
caption_position=0.72, # any RenderConfig field can be overridden
motion_intensity=0.22,
)Use the pipeline directly for custom workflows:
from src.pipeline import run_darcads_pipeline
results = run_darcads_pipeline(
product_url="https://store.com/products/thing",
variants=3,
target_seconds=25,
)
for result in results:
print(result.variant_name, result.final_video_path, result.duration)
print(result.timeline.scenes[0].narration_text)DarcAds/
├── main.py CLI
├── app.py Gradio studio
├── src/
│ ├── models.py Pydantic schemas shared across every stage
│ ├── config.py Settings, .env loading, presets, provider registry
│ ├── utils.py ffmpeg wrappers, caching, downloads, font discovery
│ ├── ingest.py Product page scraping
│ ├── llm_director.py Hook frameworks, prompting, output repair, audit
│ ├── voice.py Multi-provider TTS
│ ├── visuals.py Image framing, generation, designed frames
│ ├── avatars.py UGC lip-sync
│ ├── captions.py Word timing and the burned-in overlay layer
│ ├── assets.py Per-scene asset orchestration
│ ├── compiler.py The ffmpeg render pipeline
│ └── pipeline.py End-to-end orchestration and run manifests
├── assets/
│ ├── actors/ Your UGC presenters
│ ├── music/ Your background tracks
│ └── fonts/ Your fonts
├── tests/
└── output/ Generated videos and manifests
Individual modules are runnable for debugging:
python3 src/ingest.py --url <product-url> # inspect scraped data
python3 src/llm_director.py --url <product-url> # print the script only
python3 -m src.voice --text "Test line" --engine edge # audition a voice
python3 -m src.captions --text "Test caption" # sample a caption frame
python3 -m src.visuals --text "Test frame" # sample a designed frame
python3 -m src.avatars --list # list actorspython3 -m unittest discover -s tests # full suite
python3 -m unittest tests.test_llm_director # one moduleThe suite covers scraping and its fallbacks, script generation and repair, caption timing, image framing, the ffmpeg filter graphs and a full end-to-end render. Network access is mocked, so it runs offline. The compiler and pipeline suites perform real renders and take a couple of minutes; everything else is near-instant.
ffmpeg or ffprobe was not found on PATH
Install ffmpeg (see Install) and reopen your terminal.
The scraper returns a page title instead of a product The URL redirected, usually because the product was discontinued. Check that the URL opens a real product page in a browser. Some storefronts (Cloudflare bot protection) return 403 to any script — those cannot be scraped.
No images found DarcAds falls back to designed frames automatically. To supply your own, generate the script and edit the run manifest, or pass images through the Python API.
The voice sounds robotic
That is gTTS, the keyless default. Run pip install edge-tts — free, no key,
dramatically better.
Captions drift out of sync on long scenes
Install faster-whisper for true word alignment. Without it, timings are
estimated from character counts, which is accurate over 2-4 second lines and
loosens over longer ones.
Scenes are too short for crossfades; using hard cuts instead
Expected. A crossfade needs headroom inside each clip, so DarcAds cuts instead
of producing a broken filter graph. Lower --transition-seconds or write
longer scenes.
Renders are slow
Use --quality draft while iterating on copy. Ken Burns supersamples each
still to 2× frame size, which is the main cost; --transition none and
--captions none also speed things up.
Currency shows as $ on a non-USD store
Shopify's product JSON endpoint does not report a currency code, so DarcAds
defaults to USD. Pass the price through the Python API if you need another
symbol.
DarcAds generates advertising. That carries obligations.
- Do not fabricate claims. The director is instructed never to invent specs, prices, guarantees, medical claims or reviews, and it works from scraped data — but you are responsible for what ships.
- Only use likenesses you have the rights to. See
assets/actors/README.md. - Disclose synthetic media where your market requires it (EU AI Act, several US states, and the ad policies of TikTok, Meta and YouTube).
- License your music. Platform Content ID will find it.
- Scrape responsibly. Respect
robots.txtand terms of service. DarcAds is built to scrape your own product pages.
Contributions are welcome — see CONTRIBUTING.md. Useful places to start: additional storefront parsers, more hook frameworks, extra video providers, and caption styles.
MIT — see LICENSE. Do what you like with it, including commercially.
Third-party models and APIs you connect carry their own licences and terms.