Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fractal-audio-sync

Tooling for generating audio-reactive Mandelbrot-zoom animations in Ultra Fractal. Takes a song, analyses its beats and frequency spectrum with librosa, and emits a complete UF .upr parameter file whose camera, colors, and 3D transformations are driven by the audio.

Built incrementally while figuring out UF's parser quirks the hard way. The companion blog-post-as-comments inside the source files explains a number of UF gotchas (line length limits, integer index requirements, the difference between gradient rotation= display and actual per-stop animation, scientific-notation parser failures) that aren't in any UF documentation.

What it produces

A .upr for UF whose entry combines:

  • Mandelbrot deep zoom into a chosen target (default: Seahorse Valley at −0.7454288 + 0.1130088i) over the song's main section, going to magn 10¹⁰ by ~1:26.
  • Beat-locked zoom + rotation kicks synchronized to detected beats. Per-section pulse_scale config means the intro can be calm, verses normal, and a designated solo section can be amplified or zeroed entirely.
  • 8 frequency-band-driven color stops — each color stop's gradient-index position drifts proportionally to its paired band's cumulative energy. Sub-bass → charcoal, bass → indigo, low-mid → plum, …, air → smoky teal.
  • Section-based dynamics: 0:00–0:12 intro (no kicks), 0:12–1:26 verses (full kicks), 1:26+ "guitar solo" (camera flies through multiple Mandelbrot regions via waypoints).
  • True plane↔sphere morph during the solo via a custom 3d-morph transform (morph.uxf) — the whole composite image smoothly bends into a spinning sphere and back.
  • Per-pixel Smooth outside coloring with a curated 8-stop "moody psychedelic" palette + OrbitTraps ring overlay + Gradient (Cone) inside coloring.

Files

fractal-audio-sync/
├── README.md            ← this file
├── analyze.py           ← runs librosa on a song, dumps analysis to .npz
├── generate_upr.py      ← the main generator. reads analysis cache, emits .upr
├── morph.uxf            ← custom UF transform (plane↔sphere morph)
└── venv/                ← Python venv (not tracked)

Setup

# One-time setup
cd ~/Desktop/fractal-audio-sync   # or wherever
python3 -m venv venv
./venv/bin/pip install librosa numpy

# Custom UF transform: copy morph.uxf to your UF Formulas folder so UF can find it
cp morph.uxf "~/Documents/Ultra Fractal 6/Formulas/"

Usage

# 1. Analyze a song (only needed once per song; cached as Songname.analysis.npz)
./venv/bin/python3 analyze.py ~/Downloads/Yoursong.wav

# 2. Edit generate_upr.py to point at your analysis cache and tune parameters,
#    then run the generator
./venv/bin/python3 generate_upr.py

# 3. Open the generated .upr in Ultra Fractal
#    File → Browse → audio-mandelbrot.upr → AudioMandelbrot

The generated .upr is written to ~/Documents/Ultra Fractal 6/Parameters/audio-mandelbrot.upr so UF's browser picks it up immediately.

Configuration

All tunable parameters live at the top of generate_upr.py as ALL_CAPS constants:

Parameter What it does
PREVIEW_MODE True for fast iteration (480×854, no AA); False for full HD render
CENTER_END, MAGN_AT_SOLO_START Where the verse zoom is heading and how deep
SOLO_START_SEC When the guitar-solo flying phase begins
SOLO_WAYPOINTS List of (time, center_re, center_im, magn) tuples for the camera fly-through
MORPH_* Plane↔sphere morph timing
SECTIONS Per-section pulse_scale overrides
BAND_DRIFT_AMOUNT Max drift per color stop. Must stay below inter-stop spacing (50) or stops will cross over and the gradient flips/inverts visibly.
BAND_SAMPLE_INTERVAL Seconds between band-energy keyframes
SMOOTH_GRADIENT_STOPS The 8 paired (band → color) stops
BEAT_STRIDE 2 = half-notes, 4 = downbeats only
PULSE_MAGN_FACTOR, PULSE_ROT_DELTA Amplitude of beat-driven kicks

How the audio drives the visuals

Audio feature Drives
Detected beats (every 2nd one by default) Magnification + rotation pulses, scaled per section
8 frequency bands (40 Hz–20 kHz, cumulative energy) One color stop each — colors drift independently through the gradient
Section timestamps pulse_scale per section (intro calm, solo morph)
Solo waypoints Manual camera fly-through targets

UF parser quirks discovered along the way

Documented for future debugging. UF's .upr parser:

  1. magn= lines tolerate continuations + arbitrary length, but index="..." color=N lines have a logical-line-length cap (500 chars). Wrapping with \<newline> is supported on both, but index= still chokes if the unwrapped logical content exceeds the limit. Workaround: keep index= animations to ≤300 chars by sampling sparsely.
  2. Gradient index= keyframe values must be integers. Float values cause silent parse failures.
  3. Animating gradient rotation= alone is display-only — the rendered colors don't shift. To actually rotate the rendered gradient, every color stop's index= must be animated by the same offset in lockstep.
  4. Python's :.10g format emits lowercase 1e+10 for 11-digit integers, which collides with UF's @# time separator parsing. Use .12g or higher to avoid scientific notation in the magn range you care about.
  5. The built-in 3d-mapping transform has a discrete shape enum (Plane/Sphere/Egg) so you can't smoothly morph between them. Solution: write a custom .uxf (this repo's morph.uxf) that exposes a continuous morph ∈ [0, 1] parameter and linearly blends (u, v) between plane and sphere intersections.
  6. The built-in 3d-mapping's Plane mode is a perspective ray-plane projection ("looking at a floor"), not a flat identity passthrough. morph.uxf's Plane mode is an identity passthrough so morph=0 is indistinguishable from no transform at all.

Render-time notes

  • A full song at 720×1280 + AA takes many hours to render. Use PREVIEW_MODE = True for fast iteration at 480×854 without AA.
  • UF's render dialog lets you restrict to a frame range — useful for spot-checking specific moments (e.g. frames 2700–2900 for the morph onset).
  • For very deep zooms, UF's perturbation algorithm engages automatically with Standard.ufm > Mandelbrot (Built-in) (FastMandel) which this project uses.
  • The sphere/morph transform doesn't currently use perturbation, so deep-zoom sphere phases may render slowly.

License

Personal/artistic project. No formal license — use as you like, attribution appreciated.

About

Audio-reactive Mandelbrot zoom toolkit for Ultra Fractal - generates .upr files whose camera, colors, and 3D transformations are driven by beats and frequency-band energy from a music track

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages