Skip to main content

License Build Status PyPI

tlRender Icon tlRender

tlRender is an open source library for building playback and review applications for visual effects, film, and animation.

The library can render and playback timelines with multiple video clips, image sequences, audio clips, and transitions. Examples are provided for integrating the library with OpenGL applications.

The library is written in C++ and uses the CMake build system, with Python bindings on PyPI:

pip install tlRender

These screenshots show an example application built with tlRender. The application is comparing two images with a wipe and horizontal layout.

player 1 player 2

Features:

  • Support for timelines, image sequences, movies, and audio files
  • A/B comparison with wipe, overlay, and difference modes
  • Color management with OpenColorIO
  • Multi-track audio with variable speed and reverse playback
  • Experimental support for USD files
  • Available for Linux, macOS, and Windows

Quick start

A window that plays a timeline, movie, or image sequence given on the command line.

C++

#include <tlRender/UI/Init.h>
#include <tlRender/UI/Viewport.h>
#include <tlRender/Timeline/Player.h>

#include <ftk/UI/App.h>
#include <ftk/UI/MainWindow.h>

#include <iostream>

using namespace ftk;

int main(int argc, char** argv)
{
    try
    {
        // Create the context and application.
        auto context = Context::create();
        tl::ui::init(context);
        auto input = CmdLineArg<std::string>::create(
            "input", "A timeline, movie, or image sequence.");
        auto app = App::create(
            context, argc, argv, "simple", "Simple player example.", { input });
        if (app->hasCmdLineHelp())
            return 0;

        // Create a timeline and a player for it.
        auto timeline = tl::Timeline::create(context, Path(input->getValue()));
        auto player = tl::Player::create(context, timeline);

        // Show the player in a window.
        auto window = MainWindow::create(context, app);
        auto viewport = tl::ui::Viewport::create(context);
        viewport->setPlayer(player);
        window->setWidget(viewport);

        // Start playback and run the application.
        player->setPlayback(tl::Playback::Forward);
        app->run();
    }
    catch (const std::exception& e)
    {
        std::cout << "ERROR: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

Python

The same player, after pip install tlRender:

import feather_tk as ftk
import tlrender as tl
import sys

# Create the context and application.
context = ftk.Context()
tl.ui.init(context)
input = ftk.CmdLineArgString("input", "A timeline, movie, or image sequence.")
app = ftk.App(context, sys.argv, "simple", "Simple player example.", [input])
if app.hasCmdLineHelp:
    sys.exit(0)

# Create a timeline and a player for it.
timeline = tl.Timeline(context, ftk.Path(input.value))
player = tl.Player(context, timeline)

# Show the player in a window.
window = ftk.MainWindow(context, app)
viewport = tl.ui.Viewport(context)
viewport.player = player
window.widget = viewport

# Start playback and run the application.
player.playback = tl.Playback.Forward
app.run()

The examples directory has fuller players in both languages, with playback controls and a timeline.

Web Player

An experimental build of the example player runs in the browser, playing movies with the WebCodecs API:

The "url" query plays a movie from any host that allows cross-origin range reads. The page asks the browser for as much memory as it will give, from 2GB down; a "mem" query names the maximum in megabytes for a browser that refuses even the smallest, or for trying one. Decoded frames stay on the GPU: the browser imports each one into a WebGL texture and the viewport draws it there, with no copy through memory. The "copy" query takes the older path, which copies every frame into memory, for comparison.

The web build is etc/Web/build.sh, which builds the dependencies and the player with Emscripten; it needs emcc on the path. The Web workflow runs it on every push to main and publishes the result to the page above.

Python

The Python bindings are one wheel per platform for CPython 3.12 and later, built on the feather-tk wheel, which pip installs with them, and OpenTimelineIO. Python 3.12 or 3.13 is the easy choice for now: OpenTimelineIO has no prebuilt wheels yet for Python 3.14, or for Intel macOS, and there pip builds it from source, which needs a C++ compiler and the Python development files.

import opentimelineio as otio
import feather_tk as ftk
import tlrender as tl

The wheel has only the FFmpeg codecs that need no patent license: AV1, APV, VP8 and VP9, MPEG-2 and MPEG-4, MJPEG, FFV1, CineForm, UT Video, HuffYUV, MagicYUV, Dirac, PNG, v210, and the FLAC, Opus, Vorbis, ALAC, MP3 and PCM audio codecs. H.264, HEVC, ProRes, DNxHD, DV and AAC are not in it, so a camera movie or anything else carrying them does not open with the wheel alone. Two ways to play them: install FFmpeg yourself, and tlRender reads them with the ffmpeg and ffprobe commands on PATH; or build from source, where the codecs are compiled in.

OpenTimelineIO does not publish wheels for every platform tlRender does, so on some pip builds it from source, which needs a C++ compiler.

Without a display

A script with no window drives the player itself. Nothing updates between calls to Context.tick(), and after a seek the player's current frame is the previous one until the new one is decoded, so wait for the time asked for:

import numpy as np
import feather_tk as ftk
import tlrender as tl
import time

context = ftk.Context()
tl.init(context)
timeline = tl.Timeline(context, ftk.Path("render.0001.exr"))
player = tl.Player(context, timeline)

def frame_at(t):
    player.currentTime = t
    while True:
        context.tick()
        for frame in player.currentVideo:
            image = frame.layers[0].image if frame.layers else None
            if frame.time == t and image and image.valid:
                return image
        time.sleep(0.005)

image = frame_at(timeline.timeRange.start_time)
pixels = np.asarray(image)       # (height, width, channels), the right way up
print(pixels.dtype, pixels.shape, pixels.max())

A Timeline also takes an opentimelineio.schema.Timeline object, so a cut built or edited in Python plays without being written to a file first.

OffscreenRender draws frames the way a viewport does, a compare included, to an image you can write or read:

player.compare = [tl.Timeline(context, ftk.Path("v002/render.0001.exr"))]
options = tl.CompareOptions()
options.compare = tl.Compare.Tile
t = timeline.timeRange.start_time
player.currentTime = t
while not (len(player.currentVideo) == 2 and
           all(f.time == t for f in player.currentVideo)):
    context.tick()
    time.sleep(0.005)
image = tl.OffscreenRender(context).render(player.currentVideo, options)

info = tl.IOInfo()
info.video = [image.info]
writer = tl.WriteSystem(context).write(ftk.Path("compare.png"), info)
writer.writeVideo(t, image)
writer.finish()

Keep one OffscreenRender and render the same frames again with other options -- a wipe position, a size, a look -- and the frames are kept on the GPU rather than copied in each time. They are told apart by the image, so after changing an image's pixels in place call clearCache(). compare.py renders a tile and then sweeps a wipe this way.

Its OpenGL context is a hidden window's. On Linux with no display at all -- a container, a render farm node, continuous integration -- set SDL_VIDEO_DRIVER=offscreen in the environment, and it is an EGL context with no window system. That needs Mesa's EGL and DRI drivers (on Debian and Ubuntu, libegl1 libgl1-mesa-dri libgbm1), and uses the GPU where there is one and Mesa's software renderer where there is not. Where there is no sound card either, set SDL_AUDIO_DRIVER=dummy: audio then goes nowhere, and the audio libraries are not asked for devices, which is what prints their errors in a container.

The Python examples show the API in use. The wheel also carries the C++ libraries, headers, and CMake package, so a project with its own bindings can build against the same installation:

cmake -DtlRender_DIR="$(python -c "import tlrender; print(tlrender.get_cmake_dir())")" ...

tlRender's package finds feather-tk's in the feather_tk package beside it.

Building Dependencies

A CMake super build script is provided to build all of the dependencies from source.

Build options go in etc/Config/local.cmake, which is not tracked. For example, to enable USD (building USD also requires Python 3):

set(TLRENDER_USD ON CACHE BOOL "")

Required dependencies:

Optional dependencies:

Building on Linux

Requirements:

  • Git
  • CMake 3.31

Debian

Install system packages:

sudo apt-get install build-essential git cmake xorg-dev libglu1-mesa-dev mesa-common-dev mesa-utils libasound2-dev libpulse-dev libpipewire-0.3-dev libva-dev libdrm-dev libwayland-dev wayland-protocols libxkbcommon-dev libegl1-mesa-dev libdecor-0-dev

The Wayland packages are optional; without them SDL is built with X11 only and runs through XWayland on a Wayland desktop.

Rocky 9

Install system packages:

sudo dnf install git libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel mesa-libGL-devel pipewire-devel libva-devel libdrm-devel wayland-devel wayland-protocols-devel libxkbcommon-devel mesa-libEGL-devel libdecor-devel

Rocky 8

Install system packages:

sudo dnf install git libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel mesa-libGL-devel pipewire-devel libva-devel libdrm-devel wayland-devel wayland-protocols-devel libxkbcommon-devel mesa-libEGL-devel

Install newer compiler:

sudo dnf install gcc-toolset-13

Enable newer compiler:

scl enable gcc-toolset-13 bash

NVIDIA hardware decoding loads the driver's CUDA library at run time. On RHEL family systems that is a separate package:

sudo dnf install nvidia-driver-cuda-libs

Build

Clone the repository:

git clone https://github.com/grizzlypeak3d/tlRender.git

Run the super build script:

sh tlRender/sbuild-linux.sh

Try running the example player:

./build-Release/examples/player/player -url tlRender/etc/SampleData/MultipleClips.otio

Example running gcovr for code coverage:

gcovr -r ../../../../lib --html --object-directory lib --html-details --output gcov.html lib/tlCore lib/tlIO lib/tlTimeline

Building on macOS

Requirements:

  • Git
  • Xcode
  • CMake 3.31

Clone the repository:

git clone https://github.com/grizzlypeak3d/tlRender.git

Run the super build script:

sh tlRender/sbuild-macos.sh

Try running the example player:

./build-Release/examples/player/player -url tlRender/etc/SampleData/MultipleClips.otio

These aliases are convenient for switching between architectures:

alias arm="env /usr/bin/arch -arm64 /bin/zsh --login"
alias intel="env /usr/bin/arch -x86_64 /bin/zsh --login"

Building on Windows

Requirements:

Open the Visual Studio command console "x64 Native Tools Command Prompt for VS 2022". This can be found in the Start menu, in the "Visual Studio 2022" folder.

Clone the repository:

git clone https://github.com/grizzlypeak3d/tlRender.git

Run the super build script:

tlRender\sbuild-win.bat

Try running the example player:

set PATH=%CD%\install-Release\bin;%PATH%
.\build-Release\examples\player\Release\player -url tlRender\etc\SampleData\MultipleClips.otio

Metadata

Release files for tlRender 0.27.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tlRender 0.27.1
File Size Uploaded Provenance
tlrender-0.27.1.tar.gz 46.6 MB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for tlRender 0.27.1
File
tlrender-0.27.1-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
tlrender-0.27.1-cp312-abi3-manylinux_2_28_x86_64.whl CPython 3.12 abi3 Linux glibc 2.28+ x86-64 Details
tlrender-0.27.1-cp312-abi3-manylinux_2_28_aarch64.whl CPython 3.12 abi3 Linux glibc 2.28+ ARM64 Details
tlrender-0.27.1-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details
tlrender-0.27.1-cp312-abi3-macosx_10_15_x86_64.whl CPython 3.12 abi3 macOS 10.15+ x86-64 Details

Total release size: 259.4 MB

Release files / tlrender-0.27.1.tar.gz

Download URL tlrender-0.27.1.tar.gz
Size 46.6 MB
Tags Source
SHA-256 checksum
How to use checksums
4965e1e22461da9727b8a2460ab619ebf3cfcd5a5e9eddd6091343f971ff9d84
BLAKE2b-256 checksum
How to use checksums
64b004822cb828948b213a3215c4222d31d6f6503d2a7a5410e57e67b3f0c25f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / tlrender-0.27.1-cp312-abi3-win_amd64.whl

Download URL tlrender-0.27.1-cp312-abi3-win_amd64.whl
Size 35.6 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
2d3d407175437acf2efd35a32668eb3a467d61f4d4d8c87dfde54d1a3c2c9896
BLAKE2b-256 checksum
How to use checksums
5bdb9816153fd3e6074d22bbb6a2d30215f92dac0a25cb9ad1f971c30e044af6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / tlrender-0.27.1-cp312-abi3-manylinux_2_28_x86_64.whl

Download URL tlrender-0.27.1-cp312-abi3-manylinux_2_28_x86_64.whl
Size 48.8 MB
Tags CPython 3.12 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
75ab32737437800b4a64a3ec4965f06dea1d41441ec0d6a04488bfbb3ff789f4
BLAKE2b-256 checksum
How to use checksums
861494b3d2d42149c050bb2c5c815ebebf4cbbacfc13d13ab52b048bbcb05046
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / tlrender-0.27.1-cp312-abi3-manylinux_2_28_aarch64.whl

Download URL tlrender-0.27.1-cp312-abi3-manylinux_2_28_aarch64.whl
Size 46.1 MB
Tags CPython 3.12 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
41f526f3b25a7de77955a2022d6059975404e789312255ffd6257a7320701ea5
BLAKE2b-256 checksum
How to use checksums
76431e2012ec37d6783e57a58d9186ef5dee5c26f7ec043d2b525c9d107fa0de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / tlrender-0.27.1-cp312-abi3-macosx_11_0_arm64.whl

Download URL tlrender-0.27.1-cp312-abi3-macosx_11_0_arm64.whl
Size 38.2 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3b367aadbf714e24140d5eb42dba18eb8bfa15ceffa5d1b20d8572e62132e7d3
BLAKE2b-256 checksum
How to use checksums
66cfb5f5e10f9e669cb82c46c47cda2988f24d0759dec4c29e8b0cc0b7ab2c6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / tlrender-0.27.1-cp312-abi3-macosx_10_15_x86_64.whl

Download URL tlrender-0.27.1-cp312-abi3-macosx_10_15_x86_64.whl
Size 44.1 MB
Tags CPython 3.12 abi3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
e639c388458594b605b0210f4c6587cbf57b201ee479644f79a266cdcc925f88
BLAKE2b-256 checksum
How to use checksums
edad323691f118bc26e6b515d9caacf984fa46f9bd31ba11ad33a3aee18ef16b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for tlRender 0.27.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for tlrender-0.27.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page