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.
You'll need:
- Python 3 — python.org/downloads
- Pillow (image processing library)
Once Python is installed, open your terminal and run:
pip3 install Pillow
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
- 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.
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.
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.
- 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.
- Find the new partial set in the list and select it.
You'll need to reimport each time you generate a new file.
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
| 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. |
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. |
| 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). |
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 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. |
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
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"
| 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).
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
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.