Table of Contents
gst-projectm is a GStreamer plugin that takes an audio stream as input and produces a video stream of real-time projectM visualizations. It is not a standalone application — it is a plugin element (projectm) that runs inside any GStreamer pipeline, which means it works from the command line with gst-launch-1.0, inside applications that use GStreamer, in Docker containers, or anywhere GStreamer runs.
The plugin renders directly to OpenGL textures via FBO and outputs video/x-raw(memory:GLMemory) buffers that stay in GPU memory. Use glimagesink for zero-copy display, or gldownload when you need to hand off to CPU-side elements like video encoders.
There are several ways to get the plugin, depending on what you need:
| Option | What it does | Best for | Guide |
|---|---|---|---|
| Install a package | Adds the plugin to your existing GStreamer installation. Use with gst-launch-1.0 or any GStreamer app. |
Most users | Linux · macOS · Windows |
| Rebuild from source packages | Recompile from .dsc or .src.rpm for your exact distro and architecture, without cloning the repo. |
Distro packagers, non-x86 architectures | Linux |
| Build from source | Clone the repo and build. Full control over dependencies and build options. | Developers, custom projectM builds | Linux · macOS · Windows |
| Docker container | One-command audio-to-video conversion. No setup required. | Quick audio-to-video rendering | Docker conversion |
| Flatpak / AppImage | Self-contained bundles that ship their own GStreamer and run gst-launch-1.0 directly. No system GStreamer needed. |
Quick testing without installing anything | Linux — Portable Bundles |
Packages that install the plugin (DEB, RPM, ZIP, tar.gz) place a single shared library (libgstprojectm.so / .dll) into your GStreamer plugin path. After that, the projectm element is available in every pipeline on the system. The Flatpak and AppImage bundles are different — they are self-contained executables that include their own copy of GStreamer and are intended for quick testing, not as a way to add the plugin to your system.
The projectm element accepts these properties, set in a pipeline with property=value syntax:
| Property | Type | Default | Description |
|---|---|---|---|
preset |
string | (none) | Path to a directory of MilkDrop preset files (.milk). |
texture-dir |
string | (none) | Path to a directory of texture images used by presets. |
preset-duration |
double | 0 (indefinite) |
Seconds before auto-switching to the next preset. 0 plays indefinitely. |
soft-cut-duration |
double | 3.0 |
Duration in seconds of smooth crossfade transitions. |
hard-cut-duration |
double | 3.0 |
Minimum seconds between hard (abrupt) cuts. |
hard-cut-enabled |
boolean | false |
Allow hard cuts triggered by beats in the audio. |
hard-cut-sensitivity |
float | 1.0 |
Sensitivity for hard cut triggers (0.0–1.0). |
beat-sensitivity |
float | 1.0 |
How strongly the visualizer responds to beats (0.0–5.0). |
mesh-size |
string | 48,32 |
Visualization mesh resolution as width,height. Higher = smoother but slower. |
aspect-correction |
boolean | true |
Adjust rendering for non-square aspect ratios. |
easter-egg |
float | 0.0 |
Probability of triggering the easter egg (0.0–1.0). |
preset-locked |
boolean | false |
Lock on the current preset, disabling automatic switching. |
enable-playlist |
boolean | true |
Enable playlist-based preset switching. |
shuffle-presets |
boolean | true |
Randomize preset order. Requires enable-playlist=true. |
is-live |
string | auto |
Rendering mode: auto detects live pipelines automatically, true forces real-time rendering (may drop frames), false forces offline rendering (as fast as possible). Auto-detection is not supported on Windows or GStreamer < 1.24; set explicitly in those cases. |
min-fps |
string | 1/1 |
Lower bound for adaptive FPS in live pipelines, as a fraction (e.g. 15/1). When rendering can't keep up with the pipeline framerate, the plugin reduces its target FPS down to this floor. Only applies when is-live=true. |
Output video resolution and framerate are set through GStreamer caps, not plugin properties. The plugin outputs video/x-raw(memory:GLMemory) in RGBA format:
... ! projectm ! "video/x-raw(memory:GLMemory),width=1920,height=1080,framerate=60/1" ! ...
Run gst-inspect-1.0 projectm for the full listing from your installed version.
The plugin requires MilkDrop-compatible preset files to produce visualizations. Presets may also reference textures. Neither presets nor textures are bundled with any release package — download them separately:
| Resource | Repository |
|---|---|
| Presets (curated) | projectM-visualizer/presets-cream-of-the-crop |
| Textures | projectM-visualizer/presets-milkdrop-texture-pack |
git clone https://github.com/projectM-visualizer/presets-cream-of-the-crop.git ~/projectM-presets
git clone https://github.com/projectM-visualizer/presets-milkdrop-texture-pack.git ~/projectM-texturesThen pass the paths to the plugin:
gst-launch-1.0 audiotestsrc ! queue ! audioconvert \
! projectm preset=~/projectM-presets texture-dir=~/projectM-textures preset-duration=10 \
! "video/x-raw(memory:GLMemory),width=1920,height=1080,framerate=60/1" \
! glimagesinkYou can also use any directory of .milk preset files, including your own.
Pre-built packages are available on the GitHub Releases page.
| Variant | Description |
|---|---|
| static-gl | projectM statically linked with desktop OpenGL. Self-contained — no external projectM needed. Recommended for most desktop systems. |
| static-gles | projectM statically linked with OpenGL ES. For embedded or GLES-only environments. |
| dynamic | projectM not included. Requires projectM (>= 4.2.0) installed separately. |
Only one variant can be installed at a time — they conflict with each other.
| Format | Platform | Guide |
|---|---|---|
| DEB | Ubuntu, Debian | Linux |
| RPM | Fedora, RHEL | Linux |
| DEB Source / SRPM | Any DEB/RPM-based distro | Linux |
| ZIP | Windows (x64) | Windows |
| tar.gz | macOS (arm64) | macOS |
| Flatpak / AppImage | Linux (x86_64) | Linux — Portable Bundles |
These examples assume the plugin is installed and presets/textures have been downloaded (see above).
# Visualize a test tone (zero-copy GL display)
gst-launch-1.0 audiotestsrc ! queue ! audioconvert \
! projectm preset=~/projectM-presets texture-dir=~/projectM-textures preset-duration=5 \
! "video/x-raw(memory:GLMemory),width=1920,height=1080,framerate=60/1" \
! glimagesink
# Visualize PipeWire/PulseAudio system audio
gst-launch-1.0 pipewiresrc ! queue ! audioconvert \
! projectm preset=~/projectM-presets preset-duration=5 \
! "video/x-raw(memory:GLMemory),width=2048,height=1440,framerate=60/1" \
! glimagesinkFor offline (faster-than-real-time) conversion, use gldownload to transfer frames from GPU to CPU memory before encoding:
gst-launch-1.0 -e \
filesrc location=input.mp3 ! decodebin ! tee name=t \
t. ! queue ! audioconvert ! audioresample \
! "audio/x-raw,format=F32LE,channels=2,rate=44100" \
! avenc_aac bitrate=320000 ! queue ! mux. \
t. ! queue ! audioconvert \
! projectm preset=~/projectM-presets texture-dir=~/projectM-textures \
preset-duration=6 mesh-size=1024,576 is-live=false \
! "video/x-raw(memory:GLMemory),framerate=60/1,width=3840,height=2160" \
! gldownload ! videoconvert ! videorate \
! x264enc bitrate=50000 key-int-max=200 speed-preset=veryslow \
! "video/x-h264,stream-format=avc,alignment=au" ! queue ! mux. \
mp4mux name=mux ! filesink location=output.mp4Some elements (x264enc, avenc_aac) require additional GStreamer plugin packages on your system (e.g. gstreamer1.0-plugins-ugly, gstreamer1.0-libav).
projectM output is rendered to OpenGL textures via Frame Buffer Objects (FBO). Textures are pooled and reused across frames. Each rendered texture becomes a GStreamer video buffer pushed downstream — all video buffers stay in GPU memory as video/x-raw(memory:GLMemory). Use glimagesink for zero-copy display, or gldownload to transfer to system memory for CPU-side elements like video encoders.
The plugin synchronizes rendering to the GStreamer pipeline clock using the audio presentation timestamp (PTS) as the leading reference. Pipeline caps control the desired video framerate. The render loop is push-based to conform with GStreamer's pipeline timing model and to enable faster-than-real-time rendering. A fixed number of audio samples is consumed per video frame — for example, 735 samples per frame at 44.1 kHz yields approximately 60 FPS.
Live pipelines are auto-detected if GStreamer supports it (not supported on Windows or GStreamer < 1.24). For those cases, set is-live=true explicitly. The default mode is offline rendering (is-live=false), which renders as fast as possible. In live mode, frames may be dropped or the rendering FPS may be reduced adaptively if frame rendering cannot keep up with the pipeline framerate.
Video frame PTS offset is derived from the first audio buffer PTS or segment event, plus accumulated samples, to align video timing with the audio stream.
| Timing Source | Origin | Mode | Purpose |
|---|---|---|---|
| Audio Timestamps | Audio input | All | Determine video timing and sync |
| Sample Rate / Pipeline FPS | Audio input / Caps | All | Number of audio samples per frame and target FPS |
| Segment Info | Segment event | All | Running time and playback position for PTS offsets |
| QoS Feedback | QoS event | Live | Skip outdated frames to correct sync with downstream |
| Render Frame Drop | Render loop | Live | Drop frames that cannot be rendered in time |
| GL Frame Render Duration | Render loop | Live | EMA of frame render duration; adjusts plugin target FPS when rendering consistently exceeds the real-time budget |
| Latency Event | Render loop | Live | Inform upstream of latency changes from adaptive FPS |
| Buffer Push Clock Jitter | Render loop | Live | EMA of source pad push jitter from the scheduler; added as a correction to buffer PTS |
The plugin uses GStreamer's standard logging system. Set the GST_DEBUG environment variable to control verbosity per category. The general syntax is category:level, where levels range from 1 (errors only) to 7 (full trace). Multiple categories are comma-separated.
# See errors and warnings from the plugin
GST_DEBUG=gstprojectm:3,projectm_base:3 gst-launch-1.0 ...
# Debug frame drops and adaptive FPS in live pipelines
GST_DEBUG=renderbuffer:5,pmaudiovisualizer:5 gst-launch-1.0 ...
# Full trace (very verbose)
GST_DEBUG=gstprojectm:7,projectm_base:7,glbaseaudiovisualizer:7,pmaudiovisualizer:7,renderbuffer:7,pushbuffer:7 gst-launch-1.0 ...| Category | Source | What it logs |
|---|---|---|
projectm_base |
gstprojectmbase.c | projectM instance lifecycle, property changes, preset loading, and all log output from the projectM library itself (errors, warnings, info, debug from libprojectM are forwarded through this category) |
gstprojectm |
gstprojectm.c | Top-level plugin element: GL start/stop, per-frame render dispatch, audio/video caps |
glbaseaudiovisualizer |
gstglbaseaudiovisualizer.c | GL context discovery, FBO setup, render mode detection (live vs offline), caps changes, state transitions |
pmaudiovisualizer |
gstpmaudiovisualizer.c | Audio/video timing, QoS frame skipping, segment events, latency negotiation, buffer pool management |
renderbuffer |
renderbuffer.c | Render slot scheduling, frame drop events (eviction and timeout), adaptive FPS (EMA adjustments), render duration monitoring |
pushbuffer |
pushbuffer.c | Buffer push scheduling, clock wait jitter tracking |
buffercleanup |
bufferdisposal.c | GL buffer disposal lifecycle |
A Docker-based conversion tool is included for one-command audio-to-video rendering with no manual setup.
- Docker
- GPU acceleration: NVIDIA Container Toolkit (NVIDIA) or no extra setup (AMD/Intel)
git clone https://github.com/projectM-visualizer/gst-projectm.git
cd gst-projectm
./projectm-convert -i your-audio-file.mp3 -o output-video.mp4The first run builds the container automatically (takes a while). Subsequent runs use the cached image. Conversion time depends on audio length and settings.
| Option | Description | Default |
|---|---|---|
-d, --duration SEC |
Seconds between preset transitions | 6 |
--mesh WxH |
Mesh size for visualization | 1024x576 |
--video-size WxH |
Output video resolution | 1920x1080 |
-r, --framerate FPS |
Output frame rate | 60 |
-b, --bitrate KBPS |
Video bitrate in kbps | 8000 |
--speed PRESET |
x264 speed preset (ultrafast to veryslow) | medium |
-p, --preset DIR |
Path to custom presets directory | Default presets |
# 4K high quality
./projectm-convert -i song.mp3 -o viz-4k.mp4 --video-size 3840x2160 -b 16000 --speed veryslow
# Quick test
./projectm-convert -i song.mp3 -o viz-test.mp4 --speed ultrafast --video-size 1280x720Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project distinguishes between the plugin source code and the distribution bundle:
- The Plugin: The source code for
gst-projectmis licensed under the GNU Lesser General Public License (LGPL) v2.1 or later. This allows for broad compatibility with both open-source and proprietary applications. See LICENSE. - The AppImage/Flatpak: The binary distribution bundle is licensed under the GNU General Public License (GPL) v2.0 or later.
Note: This "upgrade" to GPL applies only to the bundle itself. It is required because the bundle includes GStreamer's "Ugly" plugin set, which contains GPL-licensed dependencies (such as x264). See BUNDLE-LICENSE.
Blaquewithaq (Discord: SoFloppy#1289) - @anomievision - anomievision@gmail.com
Mischa (Discord: mish) - @revmischa