Installing and Running

Flitter is implemented in a mix of Python and Cython and requires at least OpenGL 3.3 (Core Profile) or OpenGL ES 3.0. At least Python 3.10 is also required as the code uses match/case syntax. It is supported and tested on Apple Silicon macOS, Intel macOS, x86_64 Linux and x86_64 Windows. It is also known to build and run on AArch64 Linux, but no pre-built packages exist. It will probably work on AArch64 Windows, but this has never been tested.

Flitter is a command-line tool. It is assumed that you are comfortable using the command line on your OS of choice. You will also obviously need to be able to ensure that you have a recent Python install. Sadly, even on macOS Sonoma the system installed Python is only at version 3.9. You can normally download Python for your OS from the Python website. However, you may want to explore a package manager that can manage this sort of thing for you, like Homebrew or MacPorts.

Installing Flitter

Installing from the pre-built wheels

If you are installing on one of the supported platforms then, good news!, you can install one of the pre-built wheels in the flitter-lang PyPI package with just:

$ pip3 install flitter-lang

Otherwise you are going to need to install from the source distribution.

It is recommended to do the install into a Python virtual environment as Flitter draws on quite a few dependencies (see below). This is especially important if you are using Python 3.12 or later as it will simply refuse to install packages into the system environment. This is generally something as simple as:

$ python3 -m venv ~/.virtualenvs/flitter
$ ~/.virtualenvs/flitter/bin/pip3 install flitter-lang

Alternatively, do a user install with:

$ pip3 install --user flitter-lang

Assuming your Python install is set up well for your operating system, this should just put the flitter script into your path for you. However, if this isn’t working (on Windows, for example, you may need to have checked a box during install) then you can also run Flitter as a Python package with:

$ python3 -m flitter.engine path/to/some/flitter/script.fl

macOS and sRGB

Flitter assumes that OpenGL windows are in the sRGB colorspace. However, modern Macs generally have displays that support the wider Display-P3 colorspace, and macOS OpenGL windows will default to this.

This results in colors on macOS appearing more vivid than on other platforms. Colors in windows will not match output saved to image and video files (which are always tagged as being in the sRGB colorspace), and loaded images or video will not display accurately. Colors calculated from calibrated color functions (like colortemp() or oklch()) will also not be displayed accurately.

Flitter is capable of telling macOS to change the window colorspace to sRGB, but will only do so if the optional, macOS-only libraries pyobjc and pyobjc-framework-Cocoa are installed. You can install these manually, or automatically if you install Flitter with:

$ pip3 install "flitter-lang[macos]"

Installing from the source package

As Flitter uses Cython under-the-hood, you’ll need a working build environment with a C compiler. On macOS this means downloading Xcode. On Windows you’ll need Visual Studio. On a Debian-variant Linux box, all you should need to do is install the python3-dev package:

$ sudo apt install python3-dev

If you’ve safely navigated getting a working Python development environment, you can do the build from source with exactly the same install command as above.

Installing from the repo:

If you want to live on the bleeding edge, then you can build the current head of the main branch with:

$ pip3 install https://github.com/jonathanhogg/flitter/archive/main.zip

Running Flitter

If you’ve installed Flitter in a virtual environment, then you’ll need to ensure that the scripts/bin folder for that environment is in your system path. You can then run Flitter with a command like:

$ flitter path/to/some/flitter/script.fl

If you’ve done a local pip install instead then – with a little luck – the scripts folder is already in your path. Otherwise, you might want to try running Flitter as a Python package:

$ python3 -m flitter.engine path/to/some/flitter/script.fl

Flitter takes one or more program filenames as the main command-line arguments. If multiple programs are given then they are loaded into separate pages and the first will be executed. By default, you can switch between pages by pressing the left- and right-arrow keys while a window has focus. However, full control over page switching is actually part of the controller infrastructure of Flitter.

The supported command-line options are:

--help

See a list of these options.

--quiet | --verbose | --debug | --trace

Change the amount of logging that is written out, from least to most noisy. --verbose is useful for getting some basic runtime statistics, --debug shows a little of what is going on under-the-hood, --trace is incredibly noisy as it logs lots of object allocation and deallocation activity.

--fps FPS

Specify a target frame-rate for the engine to try and maintain. This defaults to 60fps, but it’s useful to be able to lower it if you are running an especially complex script and would prefer to run evenly at a slower rate than to have stuttering. It’s also useful when recording video output. You can also control frame-rate via a pragma.

--define NAME=VALUE

This allows a name to be bound to a constant value in the language interpreter. This is useful for controlling conditional behaviour in a program, such as enabling video output or changing the window size. VALUE may be either a string or a numeric vector, with items separated by semicolons. You will need to appropriately quote it on the command-line if using semicolons.

--screen SCREEN

Allows you to ask Flitter to open windows on a distinct screen number. This depends on your desktop setup, but generally screen 0 is the main one and they count upwards from there. Specific windows can also be placed on specific screens by adding a screen=N attribute to the !window node. This option controls the default if that is not done.

--fullscreen

Requests that windows are opened full-screen by default. Again, this can be controlled on a per-window basis with the fullscreen=true attribute to a !window node.

--vsync

Requests that windows use vertical-sync double-buffering. Also the vsync=true attribute.

--state FILENAME

Asks Flitter to save the system state periodically to a file and load again from this. This allows you to retain state across multiple sessions. This was primarily designed as a safety measure for reacting quickly to a crash during a live performance. Thankfully, Flitter is pretty resilient to crashes, but there are still some edge-cases where a code change can confuse the engine.

--resetonswitch

Throws away the internal system state when switching between different pages. Normally state is saved and restored when switching pages. Throwing the last state away can be useful if running in a kiosk mode where each page should begin again fresh.

--simplifystate SECONDS

Sets the period after which the language simplifier will simplify on static state values. The default is 10 seconds. Set this to 0 to disable simplifying on state.

--nosimplify

Completely disable the language simplifier. Generally there is no reason to do this, but running the simplifier is the slowest part of loading Flitter code and so it may be useful if very fast switching is required – particularly if a program has very large loops that are being unrolled and there is insufficient gain from doing the unrolling.

--lockstep

Turns on non-realtime mode. In this mode, the engine will generate frames as fast as possible (which may be quite slowly) while maintaining an evenly incrementing beat counter and frame clock. This is designed for saving video output from non-interactive programs. It also has a specific effect on how the physics system runs.

--runtime SECONDS

Run for a specific period of time and then exit automatically. This is particularly useful for capturing videos of a specific length unattended. In --lockstep mode, this is a period of the internal frame clock not the wall clock.

--offscreen

Requests that all !window nodes actually be silently interpreted as !offscreen nodes. This has the effect of running Flitter without any visual output. You’ll still need a windowed environment. You can conspire to run on Linux without a real windowed environment using Xvfb (and the CI tests are run just this way), but you should know that the Mesa software rasterizer is very, very slow. Flitter really wants a GPU.

--opengles

This tells Flitter to request the OpenGL ES API instead of OpenGL Core. You can use this on devices that do not support the full OpenGL API, or that are using a compatibility layer like ANGLE. At the moment, !canvas 2D drawing is not supported on OpenGL ES.

--profile

Runs the Python profiler around the main loop. See Profiling below for details.

--vmstats

Turns on logging of Flitter virtual machine statistics. This will slow down the interpreter by quite a lot. The statistics are written out on exit at the INFO logging level, which means you will also need to have at least --verbose logging turned on to see the results. Be wary of interpreting these statistics: the overhead of doing the timing can make very simple instructions that are executed millions of times appear to be a larger source of execution cost than they really are.

Developing Flitter

If you want to edit the Flitter code – or just want to keep up-to-date with current developments – ensure that cython and setuptools are installed in your runtime Python environment, clone the repo, do an editable package deployment directly from the clone directory, and then throw away the compiled files:

$ pip3 install cython setuptools
$ git clone https://github.com/jonathanhogg/flitter.git
$ cd flitter
$ pip3 install --editable .
$ rm src/**/*.c src/**/*.so

Flitter automatically makes use of pyximport to (re)compile Cython code on-the-fly as it runs. This allows you to edit the code (or pull a new version from the repo) and just re-run flitter to pick up changes.

The code is linted with flake8 and cython-lint:

$ pip3 install flake8 cython-lint
$ flake8 src tests scripts
$ cython-lint src

And the test suite can be run with pytest:

$ pip3 install pytest
$ pytest

Checking code coverage

If you want to run code coverage analysis, then you will need to do a special in-place build with coverage enabled (Cython line-tracing):

$ env FLITTER_BUILD_COVERAGE=1 python3 setup.py build_ext --inplace

Importantly, this will not work on Python 3.12 or 3.13. Python 3.12 introduced a change in the profiling API that Cython is not yet compatible with. You will need to do a parallel install of either 3.11 or 3.10 and create a second virtual environment in which to do coverage analysis.

Once you have a coverage-enabled build, you can generate a code coverage report for the test suite with:

$ pip3 install coverage
$ coverage run -m pytest
$ coverage report

You will need to re-run setup.py if you change any of the Cython code. You will need to delete all of these object files if you want to go back to using the normal pyximport automatic recompile (and you will want to do so as the coverage-enabled version of the code is significantly slower).

Profiling

Flitter has built-in support for running itself with profiling turned on: just add the --profile command line option when running it. This is best combined with --lockstep and --runtime=N to run for exactly N seconds of frame time. At the end of execution (including a keyboard interrupt), the standard Python profiler output will be printed, ordered by tottime.

However, by default, the Cython modules are not compiled with profiling support. This means that only pure Python functions will be listed in the profiler, with all time spent inside Cython code aggregated into those functions’ runtime. All of the Cython modules can be compiled with profiling by setting the FLITTER_BUILD_PROFILE environment variable to 1 and running setup.py:

$ env FLITTER_BUILD_COVERAGE=1 python3 setup.py build_ext --inplace

As for compiling with code coverage, profiling Cython code is currently not supported on Python 3.12 or 3.13. You will need to use 3.11 or 3.10 to run profiling tests.

Recompiling all Cython modules with profiling enabled may not be the best thing to do as the overhead of adding profiling support to Cython is quite high and the results will be skewed by the cost of the profiling. It is probably better to only compile profiling support into the module(s) that you are considering. This can be done with cythonize:

$ cythonize --build --inplace --force -3 --annotate -X profile=True \
    src/flitter/render/window/canvas3d.pyx

Generating the documentation

To generate a local HTML copy of the documentation, install sphinx, myst_parser and the separate flitter-pygments package (which adds syntax highlighting support for Flitter to pygments):

$ pip3 install sphinx myst_parser flitter-pygments

You can then generate the docs with:

$ sphinx-build docs build/html

They can then be read from build/html/index.html.

Python package dependencies

The first-level runtime Python package dependencies are listed below. These will all be installed for you by pip, but it’s useful to know what you’re getting into.

  • av - for encoding and decoding video

  • glfw - for OpenGL windowing

  • lark - for the language parser

  • loguru - for enhanced logging

  • mako - for templating of the GLSL source

  • manifold3d - for constructive solid geometry operations

  • moderngl - for a higher-level API to OpenGL

  • networkx - used by trimesh for graph algorithms

  • numpy - for fast memory crunching

  • pillow - for saving screenshots as image files

  • pyserial - for talking to DMX interfaces

  • pyusb - for low-level communication with the Push 2 and LaserCube

  • regex - used by lark for advanced regular expressions

  • rtmidi2 - for talking MIDI to control surfaces

  • scipy - used by trimesh for computing convex hulls

  • skia-python - for 2D drawing

  • trimesh - for loading 3D meshes

During install, pip will also use:

  • cython - because half of Flitter is implemented in Cython for speed

  • setuptools - to run the build file