// Cantis landing page sections — music-themed, with real-app-mirroring demo // =================== Helpers =================== const Mono = ({ children, dim, className = '', style = {} }) => ( {children} ); const Arrow = ({ size = 14 }) => ( ); // =================== Top Nav =================== function TopNav({ accent }) { return (
Cantis v0.4 · BETA GITHUB Download for macOS
); } // =================== Hero =================== function Hero({ accent }) { return (
FIG_00 · GENERATIVE_AUDIO

Generate
music that
actually sounds
like yours.

Cantis runs ACE-Step v1.5 natively on your Mac. Prompt a track, edit lyrics, tune the seed — everything stays on-device. No queue, no cloud, no upload.

Download · 184 MB Read the docs
FIG_01 · NOW_PLAYING
00:00 / 03:48 · 44.1 KHZ · STEREO
); } // =================== Marquee =================== function Marquee() { const items = ['ACE-STEP v1.5', 'METAL ACCELERATED', '100% ON-DEVICE', 'OPEN SOURCE', 'LYRICS EDITING', 'STEM EXPORT', '44.1 KHZ STEREO', 'TEXT → MUSIC', 'AUDIO → AUDIO']; return (
{[...items, ...items, ...items].map((it, i) => ( {it} ))}
); } // =================== Feature Grid =================== function FeatureGrid({ accent }) { return (
{/* Row 1 — Native to the metal */}
FIG_02

Native to
the metal.

Built in Swift, accelerated by MLX. A 4-minute track renders in under 30 seconds on M2 Pro. No Python, no Docker, no GPU required.

Benchmarks
SILICON_M1 / M2 / M3 / M4
{/* Row 2 — three columns: Spectrum / Generation Modes / Tags & Lyrics */}
FIG_03

FFT analyzer & live waveform

Real-time playback with waveform visualization and FFT spectrum analysis. Watch low-end, mids, and air shape themselves bar by bar.

FIG_04

Four ways to generate

text2music, cover, repaint, extract. Drop in reference audio for the last three; the model rewrites, restyles, or pulls a part out.

{['text2music', 'cover', 'repaint', 'extract'].map((m) => ( {m} ))}
FIG_05
[verse]
City lights blur in the rain
[chorus]
We were never going home
[bridge] · lang=en
{[ { t: 'genre · synthwave', c: '#ff5b9c' }, { t: 'instr · arp', c: accent }, { t: 'mood · neon', c: '#7af0a8' }, ].map((x) => ( {x.t} ))}

Lyrics, tags, structure

Verse / chorus / bridge with ISO-639 language hints. Layer genre, instrument, and mood tags to steer the model.

{/* Row 3 — Model variants & DiT knobs */}
FIG_06

Three model variants.
Pick your tradeoff.

Cantis ships three DiT variants downloaded on first launch from HuggingFace. Swap them per-render — fast iteration, polished final, or balanced base.

{[ { name: 'Turbo', steps: '8-step', detail: 'CFG-distilled', accent: true }, { name: 'SFT', steps: '60-step', detail: 'fine-tuned' }, { name: 'Base', steps: '60-step', detail: 'foundation' }, ].map((m) => (
{m.name}
{m.steps}
{m.detail}
))}
FIG_07

DiT knobs
in plain sight.

Steps, schedule shift, and CFG scale — exposed where they matter.

{[ ['steps', '8 — 100', '60'], ['shift', '1.0 / 2.0 / 3.0', '1.0'], ['cfg scale', '1 — 20', '15.0'], ['variance', '0.0 — 1.0', '0.40'], ['seed', 'i32', '3506387122'], ].map(([k, range, def]) => (
{k} {range} {def}
))}
{/* Row 4 — Audio import / Presets+History / Export */}
FIG_08
DROP_REFERENCE_AUDIO
WAV · M4A · MP3

Drop a reference

Drag-and-drop source audio for cover, repaint, or extract modes — and the model takes it from there.

FIG_09

Presets & history

Save reusable generation configs. Browse, search, and favorite past tracks — every render kept locally with its prompt and parameters.

FIG_10

Export to your DAW

WAV, AAC (.m4a), or ALAC (.m4a) via Apple's encoder. Drag the file straight into Logic, Ableton, or Reaper.

{/* Row 5 — Trust strip */}
{[ { fig: 'FIG_11', title: 'Sandboxed', body: 'App Sandbox enabled. No Python subprocess, no helper daemons. Cantis is a single signed Mac app.' }, { fig: 'FIG_12', title: 'Pure Swift / MLX', body: 'On-device inference via mlx-swift. First launch fetches MLX-converted weights from HuggingFace; everything after is offline.' }, { fig: 'FIG_13', title: 'Log viewer', body: 'A built-in log window for debugging and monitoring renders — no Console.app spelunking.' }, ].map((it, i, arr) => (
{it.fig}

{it.title}

{it.body}

))}
); } // =================== Studio Demo (mirrors real app) =================== const APP_PRESETS = [ { id: 'neon', label: 'Neon Drive', sub: 'Retro electronic mo...', genre: 'synthwave', bpm: 124, key: 'F# min', dur: '3:48' }, { id: 'lofi', label: 'Late Night Lofi', sub: 'Warm vinyl and mail...', genre: 'lo-fi', bpm: 78, key: 'A min', dur: '3:12' }, { id: 'cine', label: 'Cinematic Lift', sub: 'Wide, emotional trail...', genre: 'orchestral', bpm: 96, key: 'D min', dur: '4:22' }, ]; const RECENTS = [ 'synthwave track', 'synthwave track', 'chill lofi piano', 'chill lofi piano', 'chill lofi piano', 'chill lofi piano', ]; function StudioDemo({ accent }) { const [tab, setTab] = React.useState('Generate'); const [preset, setPreset] = React.useState(APP_PRESETS[0]); const [prompt, setPrompt] = React.useState('cinematic orchestral build with deep percussion and hopeful climax'); const [tags, setTags] = React.useState(['cinematic', 'orchestral', 'uplifting']); const [duration, setDuration] = React.useState(45); const [variance, setVariance] = React.useState(0.4); const [steps, setSteps] = React.useState(60); const [shift, setShift] = React.useState(1.0); const [cfg, setCfg] = React.useState(15); const [seed, setSeed] = React.useState('3506387122'); const [generating, setGenerating] = React.useState(true); const [genStep, setGenStep] = React.useState(4); const [playing, setPlaying] = React.useState(false); const [playhead, setPlayhead] = React.useState(0); const [loop, setLoop] = React.useState(true); React.useEffect(() => { if (!generating) return; const id = setInterval(() => { setGenStep((s) => { if (s >= steps) { setGenerating(false); return steps; } return s + 1; }); }, 80); return () => clearInterval(id); }, [generating, steps]); React.useEffect(() => { if (!playing) return; let raf; const start = performance.now() - playhead * 35000; const tick = () => { let t = ((performance.now() - start) / 35000); if (t >= 1) { if (loop) t = 0; else { setPlaying(false); t = 1; } } setPlayhead(t); raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [playing, loop]); const startGenerate = () => { setGenStep(0); setGenerating(true); setPlaying(false); setPlayhead(0); }; const cancel = () => { setGenerating(false); }; const removeTag = (t) => setTags((tt) => tt.filter((x) => x !== t)); const bars = React.useMemo(() => { const seedNum = parseInt(seed.slice(-4), 10) || 1; return Array.from({ length: 110 }).map((_, i) => 0.15 + Math.abs(Math.sin(i * 0.32 + seedNum * 0.001) * 0.55 + Math.cos(i * 0.11 + seedNum * 0.002) * 0.4)); }, [seed]); const progressFrac = genStep / steps; const minimapBars = bars.slice(0, 80); return (
FIG_14 · CANTIS_APP

The studio,
in one window.

TRY_BELOW · CLICK_ANYTHING
{/* App window — min-width + overflow-x:auto makes it scroll on narrow screens */}
{/* Title bar */}
{/* Body */}
{/* Sidebar */}
Workspace
{[ { name: 'Generate', icon: '✨' }, { name: 'History', icon: '⟲' }, { name: 'Audio to A...', icon: '♪' }, { name: 'Settings', icon: '⚙' }, ].map((it) => { const active = tab === it.name; return ( ); })}
Presets
{APP_PRESETS.map((p) => ( ))}
Recent
{RECENTS.map((r, i) => (
{r}
Apr 30, 2026 at {3 + (i % 4)}:0{i}…
))}
{/* Center column */}
{tab}
{generating ? (
Step {genStep} / {steps}
Generating
) : ( )}
Prompt