image(...) — pictures in a score

A picture drawn straight into the SVG is fine when it never changes. image() is for when it does: a folder of plates dealt out in a different order each performance, a slideshow that advances while the playhead is inside a region, a picture chosen by the same kind of pattern that chooses a sound.

image(src:cloud.png)                       one file, shown when triggered
image(path:plates, mode:shuffle)           a folder, a different one each pass
image(src:Pseq(a.png, b.png, inf))         an explicit order
image(path:plates, mode:seq, dur:2)        a slideshow, two seconds a frame

Where the pictures live

In the project's images/ folder, beside audio/ and video/:

myScore/
├── score.svg
└── images/
    ├── cover.png
    └── plates/
        ├── plate-01.png
        ├── plate-02.png
        └── plate-03.png

path:plates takes everything in that subfolder. PNG, JPEG, GIF, WebP, AVIF, SVG and BMP are recognised. Files are fetched ahead of time, so the picture is ready before the playhead reaches it.


The element is the frame

The cue goes on an ordinary SVG element — usually a rectangle you draw in Inkscape where the picture should appear. Its box is the frame: the picture is placed there and sized to it, and the rectangle itself is hidden once the picture is in. Move or resize the rectangle and the picture follows.

<rect id="plates" data-oscilla="image(path:plates, mode:shuffle)"
      x="4200" y="180" width="900" height="600"/>

Parameters

Key Default Description
src One file, or a pattern of files: Pseq, Prand, Pxrand, Pshuf, Pchoose
path A folder inside images/. Every picture in it becomes the pool
glob Filter the folder, * and ? wildcards: glob:plate-*.png
mode shuffle Which one next, see below
fit contain contain shows the whole picture letterboxed, cover fills the box and crops, fill stretches
dur Seconds per picture. Without it, one picture per trigger; with it, a slideshow
fade 0 Fade in and out, in seconds. fadein / fadeout set them separately
opacity 1 0 to 1
uid element id Cues sharing a uid share a position in the folder, so two frames can deal from one pack
trig playhead As for any cue: playhead, click, auto, or another object's uid

Before it is triggered

By default the box shows the first picture of whatever it is pointed at, so a score at rest reads as the score rather than a row of empty rectangles. The real sequence replaces it when the cue fires.

image(path:horse)                     first frame showing until triggered
image(path:horse, placeholder:0.35)   the same, dimmed to mark it "not yet"
image(path:horse, placeholder:0)      an empty box until the playhead arrives

Fullscreen cues are never given a placeholder — one covering the window before its time would be worse than an empty box.

Scrubbing back

Move the playhead back to before a trig:playhead image cue and it stops: the slideshow ends, the folder starts again from the top, and the placeholder comes back. The score returns to how it looked before that cue had played, which is what rewinding past something ought to mean. Cues driven by a hand (trig:click) or by another object are left alone.

Fullscreen

size:fullscreen puts the picture over the whole window instead of inside a box in the score. The score keeps scrolling underneath, so the playhead does not lose its place while the picture is up.

image(src:plate.jpg, size:fullscreen, close:1, fade:0.4)
image(path:plates, size:fullscreen, dur:3, hold:30, backdrop:1)
image(src:ground.jpg, size:fullscreen, layer:under, opacity:0.4)
Key Default Description
size fullscreen (or fs) for the whole window
layer over over the score, or under it — a ground the notation is read against
backdrop 1 Dim the score behind the picture. backdrop:0 leaves it visible
close 0 A close button in the corner
clickclose 0 Click the picture itself to dismiss it
hold Seconds up, then it goes by itself

Getting out again, in order of how deliberate they are: hold: takes it away on a timer, close:1 gives a button, clickclose:1 lets a press anywhere on the picture dismiss it, and Escape always works — a fullscreen picture is the first thing Escape backs out of, one press per picture, the same as a fullscreen video.

dur: and hold: do different jobs and combine: dur: is the step of a slideshow, hold: is how long the whole thing stays. image(path:plates, size:fullscreen, dur:2, hold:20) deals a new plate every two seconds and gives up after twenty.

Layering follows the video cue: backdrop, picture, close button in that order, above the score. layer:under drops the picture beneath the notation instead.


Modes


Sharing a pack between frames

Two cues with the same uid draw from one pool and one cursor, so three frames on a page can show three different pictures from the same folder rather than three copies of the same one:

image(path:plates, uid:pack, mode:shuffle)
image(path:plates, uid:pack, mode:shuffle)
image(path:plates, uid:pack, mode:shuffle)

Examples

// a cover that appears once and stays
image(src:cover.png, fit:cover, fade:1)

// a new plate at every rehearsal mark, never repeating within a pass
image(path:plates, mode:shuffle, fade:0.3)

// a slideshow while the playhead is in this region
image(path:film, mode:seq, dur:0.5)

// performer-triggered
image(path:plates, mode:random, trig:click)

// fired by another object crossing it
image(path:plates, trig:readPH)

Opacity from another object

opacity takes a follow(), so anything publishing a 0-1 signal can dim the picture live:

image(uid:plate, src:horse.jpg, opacity: follow(fader.t))

The two cues know nothing about each other — the fader publishes fader.t, the image follows it. Any 0-1 source works: an o2p() dot's travel along its path (follow(dot.t)), a controlXY handle (follow(pad.y)), mic amplitude (follow(adc.amp)), or an incoming OSC address (follow(/pedal/1)).

In a sequence the binding follows the pictures: each new frame is placed at the current value rather than at the one written in the score.

Picking the plate with a signal

A folder is normally dealt out — mode:seq steps through it, mode:shuffle reorders it. index: turns it into a lookup table instead: something else chooses which plate is showing.

image(uid:plate, path:horse, index: follow(dial.t))

With no range, follow() spans the whole folder: 0 is the first file, 1 the last. Give numbers to use part of it — follow(dial.t, 3, 8) scrubs the middle six plates only. Values are rounded to the nearest file and clamped, so a source that overshoots holds at an end rather than going blank.

While index: is bound, dur: is ignored — something else is doing the advancing. Reloading the same plate is skipped, so a slow-moving source does not thrash the image element.

This is how you scrub a frame sequence by hand, or drive it from anything that moves: a rotate()'s angle, a trans() bar's progress, a controlXY handle, an incoming OSC pedal.

What an image publishes

An image() reports which plate it is on, so the pictures can drive something else in turn:

Channel Meaning
index which file, 0-based within the folder
t the same position as 0–1
image(uid:plate, path:horse, mode:seq, dur:0.12)
synth(uid:s1, wave:sine, freq: follow(plate.t, 200, 900), amp:0.1)

A src: pattern of explicit filenames has no folder to be a position in, so it publishes nothing — only a path: folder (or a file list) has an index.

Demo

See demo-image — pictures from a file, a folder and a pattern, using Muybridge's galloping horse.

http://localhost:8001/?project=demo-image

Tip: use ← → or ↑ ↓ to navigate the docs