Placement

A video is a timeline, and placement is putting things on it: this clip from 0 to 8 seconds, that image for the next 4, a music track underneath, a line of text at the bottom. This page covers the VideoBuilder, the object the timeline lives in, and every way of placing something on it.

The VideoBuilder

Every video starts by creating a VideoBuilder. It needs the project, and optionally a canvas size and a background color:

from imaginaryarts import VideoBuilder, Color

video = VideoBuilder(
    project=project,
    size=(1080, 1920),                    # width, height in pixels; 1080 by 1920 is the default
    background=Color(system="black"),     # what shows where nothing is placed
)

You then add things to it, and finally call render():

video.add_video(footage, at=0)
video.add_audio(music, volume=0.25)
output = video.render()      # the path of the finished mp4

Every call returns something you can keep chaining, so the style is "one line per thing on the timeline".

Time

All times are integers in milliseconds. 5000 is five seconds. A span of time is an interval, written as a two-item list [start, end]. An end of None means "until the end of the video":

interval=[0, 4000]       # the first four seconds
interval=[4000, None]    # from four seconds to the end

at= places something at a moment and lets it run for its own natural length.

Images

video.add_image(image, interval=[0, 4000])

image is a generated image (see Image Generation), an ImageFile, or just a path. An image has no length of its own, so it always needs an interval.

Video clips

video.add_video(clip, at=0)                       # play once, at its natural length
video.add_video(clip, interval=[0, 10000])        # fill exactly this interval

Pass exactly one of at= and interval=. When an interval is longer than the clip, fit decides what happens after the clip runs out:

fitBehaviour
"loop"Play the clip again from the start. The default.
"freeze"Hold the last frame.
"none"Show the background color.

A clip's own sound plays too. volume scales it, and volume=0 mutes it:

video.add_video(clip, interval=[0, 10000], fit="freeze", volume=0.6)

Clips that don't match the canvas are scaled and cropped to fill it, so a 16:9 file placed on a vertical canvas loses its sides rather than getting black bars.

Trimming

To use only part of a clip or audio file, trim it before placing it. trim takes an interval in the file's own time and returns a trimmed copy, leaving the original untouched:

intro = footage.trim([0, 3000])              # the first three seconds
rest = footage.trim([12000, None])           # from twelve seconds to the end

video.add_video(intro, at=0)
video.add_video(rest, at=3000)

The same call works on VideoFile, on generated clips, and on AudioFile. See Working with Files.

Audio

video.add_audio(music, volume=0.25)
video.add_audio(rain, volume=0.4, interval=[8000, 20000])

Audio tracks are mixed under everything else. volume is a multiplier, 1.0 being the file's own level. Without an interval the track plays for the whole video, looping if it is shorter than the video. With an interval it plays only in that window, looping to fill it.

The narration is not added this way. A voiceover is placed with tile() or add_voiceover, which know where each piece of speech belongs; see Timing.

Text

Static text is a line that sits on screen, such as a title, a source line or a watermark:

from imaginaryarts import Position

video.add_static_text("maximsbook.com", position=Position.BOTTOM)
video.add_static_text("Part one", position=Position.MIDDLE, interval=[0, 3000])

position is Position.TOP, Position.MIDDLE or Position.BOTTOM. Without an interval the text shows for the whole video. Fonts, sizes and colors are covered in Typography, and captions that follow the voice in Creating subtitles.

Layers

Visuals have a z_index, which is 1 by default. Layer 1 is the base track: the visuals on it must not overlap, and any gaps between them show the background color. Higher layers are drawn on top, in order, and can overlap the base freely. That is how b-roll goes over a talking head:

video.add_video(talking_head, at=0)                                  # base, z_index=1
video.add_image(b_roll, interval=[5200, 11200], z_index=2)           # drawn over it
video.add_video(rocket_clip, at=18000, z_index=2)

Two visuals on the same layer may not overlap. An overlay covers the whole frame; while it shows, the base is hidden. Only FadeIn and FadeOut can be attached to an overlay, since the other effects are transitions between neighbours on the base track.

Static text is on layer 2 by default, which keeps a watermark above the pictures and the captions.

Looks and motion

A few things are placed on the timeline without being content: color grades, film looks and camera motion apply to whatever is showing during their interval.

video.add_motion(CameraMotion.KenBurns(), interval=[0, 5000])
video.add_grade(Grade.VintageFade(), interval=[0, None])
video.add_film_look(FilmLook.LoFi(), interval=[0, None])

They are described in Camera Motion and Color Correction.

Rendering

output = video.render()

render() writes the mp4 into the project's output folder and returns its path. It never overwrites: the first render of a project is <id>.mp4, the next <id>_2.mp4, and so on, so you can compare attempts.

Rendering refuses two kinds of empty video: one with no visuals at all, and one with no sound at all (no voiceover, no audio track, and no clip with audible sound). Everything else, such as an image with a music track, is fine.

Everything at a glance

CallPlaces
add_image(image, interval=, z_index=1)A still image for an interval.
add_video(video, at= or interval=, fit="loop", volume=1.0, z_index=1)A clip, once or filling an interval.
add_audio(audio, volume=1.0, interval=None)A music or sound track.
add_voiceover(segment, at=)A piece of narration. See Timing.
add_static_text(text, text_style=, position=, interval=None, z_index=2)A fixed line of text.
add_dynamic_text(DynamicText(...))Captions that follow the voice.
add_motion(motion, interval=)Camera motion over an interval.
add_grade(grade, interval=) / add_film_look(look, interval=)A look over an interval.
render()Writes the mp4.