Skip to content

madrona-labs/img2utu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 

Repository files navigation

img2utu

Convert any image into a Sumu .utu partial set. The brightness of each pixel maps to the amplitude of a partial — dark areas become loud, bright/white areas become silent.


Setup

1. Requirements

You'll need:

Once Python is installed, open your terminal and run:

pip3 install Pillow

2. Download the script

Save img2utu.py somewhere you'll remember. A good spot is inside your Sumu Partials folder, in a subfolder called img2utu — that way your output files land right where Sumu can find them.

On Mac, the Partials folder can be found here: ~/Music/Madrona Labs/Sumu/Partials and on Windows: C:/AppData/Roaming/Madrona Labs/Sumu/Partials

3. Open a terminal

  • Mac: open the Terminal app (search "Terminal" in Spotlight)
  • Windows: open Command Prompt or PowerShell

Navigate to the folder where you saved the script using the cd command:

cd /path/to/your/img2utu/folder

Tip: On Mac you can type cd (with a space) and then drag the folder from Finder into the terminal window — it'll fill in the path for you.


Basic usage

Once you're in the right folder:

python3 img2utu.py path/to/your/image.png

Pretty much any common image format works (PNG, JPG, TIFF, WebP, etc.). See the Pillow docs for the full list.

If your image file is giving you issues, convert to .jpg when in doubt.

If it works, the terminal will print a summary and a .utu file will appear in the same folder as the script.

Batch processing a folder

Point the script at a folder instead of a single file to process every image inside it in one go:

python3 img2utu.py path/to/your/images/

Each image produces a .utu file with the same base name, saved in the same folder by default. Use -o to send all outputs to a different directory:

python3 img2utu.py path/to/your/images/ -o path/to/output/

All the usual flags apply to every image in the batch.

Loading it in Sumu

  1. Open Sumu and click the "..." at the top right of the partials window, then "Import folder...". You'll want to select your entire Partials folder to keep things tidy.
  2. Find the new partial set in the list and select it.

You'll need to reimport each time you generate a new file.


Options

You can customize how the image is interpreted by adding flags to the command. Flags come after the image path and can be in any order.

They look like this:

  • Short form: a single dash + one letter, e.g. -q
  • Long form: two dashes + a word, e.g. --quantize

Some flags are bare switches (just include them to turn them on). Others take a value after them.

Example with several flags:

python3 img2utu.py myimage.png -q --gamma 1.6 -d 2

Run --help to see all options at a glance:

python3 img2utu.py --help

Output

Flag Description
-o, --output-path OUTPUT Save the .utu to a specific path and filename. By default it's saved next to the script with the same name as the source image. Watch out: re-running without -o will overwrite the previous file.
--debug Bake the parameters used into the output filename, so you can tell files apart at a glance.
-P, --print-options Like --debug, but only appends options that differ from their defaults. Useful for keeping filenames short while still tracking what you changed. If --debug is also passed, it takes priority.

Resize mode (-r)

The Sumu partials window is a 10:3 aspect ratio. If you want a perfect fit, crop your image to those proportions before running the script. Otherwise, use -r to choose how the script handles the mismatch.

python3 img2utu.py myimage.png -r fill
Mode Behavior
fit-width (default) Scales to fill the width. Excess is trimmed from the bottom; if the image is shorter, the bottom is padded white.
fit-height Scales to fill the height. Excess is trimmed equally from both sides; if narrower, both sides are padded white.
fit Shows the entire image with no cropping. The shorter axis is padded with white.
fill Fills the entire window with no padding. The longer axis is cropped.
stretch Distorts the image to fill the window exactly. Proportions are not preserved.
tile Uses fit logic for one tile, then repeats it to fill the window.
tile-reflect Same as tile but mirrors alternating columns, so the image plays forward then backward as it repeats.

Resolution & duration

Flag Description
-d, --density MULTIPLIER Multiplies the horizontal resolution. Default is 1 (213 columns). At 1×, data pixels are square. Higher values add more time detail, but amplitude is scaled down automatically to compensate for the denser grid.
-w, --width WIDTH Set the canvas width directly in pixels instead of using a density multiplier.
-H, --height HEIGHT Canvas height in pixels (max 64, since that's the partial limit). Default: 64.
-D, --duration SECONDS Total length of the partial set in seconds. Default: 8 (which is 16 beats at 120 BPM).

Frequency range

The script picks 64 frequencies between a lower and upper limit, spaced logarithmically so they appear as evenly spaced rows in the partials window.

Flag Description
-l, --freqmin HZ Lowest frequency. Default: 20 Hz.
-x, --freqmax HZ Highest frequency. Default: 20000 Hz.
-f, --fundamental HZ The root frequency used as the base pitch for quantized tunings. Default: 220 Hz (A3). Only affects output when quantization is enabled.

Quantization

Quantization snaps the script's frequency grid to actual musical pitches, so your image produces tonal, in-tune sounds instead of a continuous spectrum.

Flag Description
-q, --quantize Enable quantization. Bare -q turns it on.
-t, --tuning TUNING Tuning system to use. Options: equal (default), just, pythagorean, or a path to a .scl (Scala) file for any custom tuning.
-s, --scale INDEX Which scale to apply. Pass a number from the list below, or a custom mask string (see below). Default: 0 (Chromatic — all notes).
--quantize-lowest By default the 64 pitches are spread evenly across the full frequency range. Add this flag to instead start from the lowest qualifying note and work upward — useful if you want the partial set to follow a specific pattern exactly.

Built-in scales

 0  Chromatic
 1  Ionian (major)
 2  Dorian
 3  Phrygian
 4  Lydian
 5  Mixolydian
 6  Aeolian (natural minor)
 7  Locrian
 8  Melodic minor
 9  Harmonic minor
10  Major pentatonic
11  Minor pentatonic
12  Quartal
13  Quintal
15  Major triad
16  Minor triad
17  Dominant 7th
18  Major 7th
19  Minor 7th
20  Whole tone
21  Augmented
22  Octatonic (half-whole)
23  Octatonic (whole-half)
24  3rd Messiaen mode
25  4th Messiaen mode
26  5th Messiaen mode
27  6th Messiaen mode
28  7th Messiaen mode
29  I IV V
30  ii V I
31  I vi IV V
32  I V vi IV

Custom mask strings

You can define your own note selection with a string of 1s (note on) and 0s (note off), starting from the fundamental (A 220 Hz by default, or whatever you set with -f). The mask repeats across the entire frequency range.

python3 img2utu.py myimage.png -q -s "101010010100"

Amplitude & brightness

Flag Description
-a, --ampmax VALUE The amplitude assigned to the darkest pixels. Default: 12 at 1× density, scales down as density increases. Passing this flag overrides the automatic density scaling.
-A, --amp-anchor VALUE The amplitude assigned to the single darkest pixel in the image — acts as a fixed reference point. Default: 20.
-g, --gamma VALUE Gamma curve applied to brightness before mapping to amplitude. Default: 1.3. Increase this if your output sounds too quiet or sparse.
-b, --skip THRESHOLD Pixels at or above this brightness value (0–255) are filtered out. Default: 255 – no effect. Depending on the image, some threshold levels may produce .utu files that crash your DAW when loaded. Use at your own risk!

Transparency: If your image has transparent areas, they are treated as white (silent).


Presets

Flags can be a lot to type for complex setups. Presets let you save and reload parameter sets.

Flag Description
--save-preset FILE Save the current parameters to a .json file. Presets can live in your Partials folder — Sumu will ignore them.
--load-preset FILE Load a saved preset as base defaults. Any flags you add on the command line will still override the preset.
--extract FILE Read and print the parameters stored inside an existing .utu file. Only works for files made with img2utu. Combine with --save-preset to turn those parameters into a reusable preset file.

Example — extract params from a .utu and save as a preset:

python3 img2utu.py --extract mysound.utu --save-preset mysound-preset.json

Example — use a preset as a starting point, then tweak one thing:

python3 img2utu.py newimage.png --load-preset mysound-preset.json --gamma 2.0

Full example

python3 img2utu.py ~/Desktop/myimage.png -q -t just -s 1 -d 2 -D 16 --debug

This takes myimage.png, quantizes it to just intonation in a major scale, doubles the horizontal resolution, sets the duration to 16 seconds, and bakes the parameters into the filename.

About

Script to convert an image into .utu partials.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages