Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Video Art Player

A video player for looping video art. Its one job is the thing ordinary players get wrong: crossing the loop point without a gap in the sound, a stutter in the picture, or a black frame.

Made for gallery walls, projections and screens that have to run unattended for hours.

Platforms. The native app is macOS only (macOS 12 or newer) — it is built on Apple's AVFoundation. For Windows and Linux there is the browser player in web/, which uses the same seamless technique and runs fullscreen through tools/kiosk.py.

Why ordinary players stutter

QuickTime Player, VLC and IINA all restart their playback pipeline when a file loops: they seek back to the start, flush the audio buffers, and refill them. You hear a short silence and often see a held frame. For a film that is invisible; for a one-minute loop that repeats sixty times an hour it is the thing your audience notices most.

Two smaller causes pile on top of it. Lossy audio codecs such as AAC begin with a few milliseconds of priming samples that get decoded again on every restart. And a loop whose last frame is not the visual neighbour of its first frame will jump no matter how good the player is — see docs/seamless-looping.md for how to author one that does not.

The macOS app

Uses AVPlayerLooper, which enqueues and prerolls the next cycle while the current one is still playing. Nothing is torn down at the loop point, so audio and video simply continue.

./macos/build.sh                      # writes build/Video Art Player.app

Requires the Xcode command line tools (xcode-select --install). There are no other dependencies and nothing to install — the result is a self-contained 140 KB app bundle.

Put the app next to your video and double-click it: it picks up the video beside the bundle and plays it fullscreen, looping forever. Or drive it from a terminal:

"build/Video Art Player.app/Contents/MacOS/VideoArtPlayer" loop.mov --screen 1
Option Effect
--screen N Which display to use, 0 is the main one
--fill Crop to fill the display instead of letterboxing
--window, -w Windowed instead of fullscreen
--volume V Volume, 0.0 to 1.0
--mute Start muted
--test Measure the gap at the loop point, print it, exit
--help Full usage

Keys while playing: Esc/Q quit · Space pause · M mute · F windowed/fullscreen · C cursor.

The app keeps the display and the system awake for as long as it runs, and hides the cursor and the menu bar in fullscreen. Given several files it prefers .mov over .mp4, because uncompressed audio has no priming delay to stumble over.

Because the app is not notarised, macOS blocks it on first launch: right-click → Open, or xattr -dr com.apple.quarantine "Video Art Player.app".

Proving it works

--test seeks to just before the end, then compares wall-clock time against media time across the loop point. A player that flushes its pipeline shows up as a positive gap.

$ VideoArtPlayer loop.mov --test
test: loop point crossed, gap -9 ms (wall clock 1.85 s vs media 1.86 s), rate=1.0

Anything under a few tens of milliseconds is measurement noise. It exits 0 when the gap is within tolerance, so you can put it in CI or a pre-show check.

The browser player — macOS, Windows, Linux

web/index.html is a single self-contained file. Open it, drop your video in, and it plays fullscreen and loops.

If you also drop in a separate audio file, the video plays muted and the soundtrack loops through Web Audio as an AudioBufferSourceNode, which continues sample-exactly across the loop point instead of restarting. The video is then softly locked to the audio clock by nudging its playback rate a fraction of a percent — never by seeking, which would be visible.

For an installation, let the launcher do it:

python3 tools/kiosk.py loop.mp4 soundtrack.wav

That serves the player and your media on localhost (with byte-range support, so 4K files seek properly), then opens Chrome, Edge, Brave or Firefox in kiosk mode with autoplay allowed. Python 3 is the only requirement. --browser, --port, --fill and --no-browser are there when you need them.

Keys: Space pause · F fullscreen · M mute · C cursor · D diagnostics (loop count and audio/video drift).

Starting it automatically

  • macOS — System Settings → General → Login Items, or a launchd agent.
  • Windows — put a shortcut to pythonw tools\kiosk.py … in shell:startup, or use Task Scheduler with "At log on".
  • Linux — a ~/.config/autostart/*.desktop entry, or a systemd --user service.

Which one to use

macOS app Browser player
Platforms macOS 12+ macOS, Windows, Linux
Dependencies Xcode CLT to build, none to run a browser; Python 3 for kiosk mode
Gapless audio always, from the file itself with a separate audio file
Multiple displays --screen N, one instance per display one browser window per display
Best for a Mac driving a gallery screen anything that is not a Mac

Verified here on macOS 26 (Apple silicon) for the app, and in Chromium for the browser player. The browser player uses only standard web APIs, so Chrome, Edge and Firefox on Windows and Linux should behave the same — but that has not been tested on those platforms.

Authoring a loop worth playing

A seamless player cannot rescue a video that does not loop. The short version: build all motion from components whose period divides the loop exactly, so the last frame lands back on the first, and quantise every pitch in the soundtrack to a whole number of cycles per loop. No crossfades, no fade to black.

The full method, with the arithmetic and the encoder settings that preserve it, is in docs/seamless-looping.md.

Example: Silkstream

examples/silkstream/ contains a complete generative work built this way — a one-minute 4K loop with an ambient soundtrack, where the last frame is bit-identical to the first and the audio seam is smaller than a single sample step. Both generators are seeded, so rendering reproduces exactly the same artwork.

Silkstream

License and credits

MIT — see LICENSE.

Created by Serge de Beer. The Silkstream example is his artwork; the code that generates it is MIT-licensed along with the rest of the repository.

About

Seamless looping video player for video art — no gap, no stutter, no held frame at the loop point. Native macOS app plus a cross-platform browser player.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages