Skip to main content

Zentris

A Tetris game inspired by Tetris Effect, but playing any playlist you like: your own music folder or a YouTube playlist. Each song is analysed ahead of time (tempo, key, structure: intro, verse, build, chorus, drop...) and the game stages it: pieces fall on the beat, the pace follows the song's energy, and a procedurally generated 3D scene evolves with every section. Built in C++ / OpenGL 3.3.

Nine scenes generated by the game

Nine scenes generated by the game. Every song, and every run, gets its own scene.

Install

PyPI

Prebuilt packages for Linux, macOS (Intel and Apple Silicon) and Windows are on PyPI.

Run it directly with uv, without installing:

uvx zentris "https://www.youtube.com/playlist?list=PLWHPu2N_Gb2lbZ8-7sYKSEbDlb3UiAVye"
uvx zentris ~/Music
uvx --from zentris zenscope ~/Music      # the analysis viewer

Or install it with pipx (or uv tool install zentris):

pipx install zentris
zentris "https://www.youtube.com/playlist?list=PLWHPu2N_Gb2lbZ8-7sYKSEbDlb3UiAVye"
zentris ~/Music             # with no argument it plays ./audio, or ~/Music
zenscope ~/Music            # the analysis viewer

Quote YouTube URLs: & is special in the shell. The package brings yt-dlp and an ffmpeg build for YouTube playlists; YouTube also needs a JavaScript runtime (Deno or Node.js) on your system.

Build from source

Dependencies (Debian/Ubuntu): sudo apt install cmake g++ libglfw3-dev libglew-dev (miniaudio and stb are vendored in third_party/).

cmake -S . -B build && cmake --build build -j
./build/zentris                 # plays every .mp3/.wav/.flac in ./audio, in order (--shuffle for random)
./build/zentris song.mp3 ~/Music --fullscreen

YouTube playlists (or single videos) work too, through yt-dlp (install it with pipx install "yt-dlp[default]"; YouTube also needs a JavaScript runtime such as Deno or Node.js, and Node.js is picked up automatically). Songs are downloaded when queued and cached as MP3 in ~/.cache/zentris/youtube/. Downloading from YouTube is against its terms of service: personal use only.

./build/zentris "https://www.youtube.com/playlist?list=PLWHPu2N_Gb2lbZ8-7sYKSEbDlb3UiAVye"   # quote it: & is special in the shell
./build/zenscope "https://www.youtube.com/watch?v=..."

Other options: --shuffle (random song order; default is in order: folders alphabetically, playlists in their order), --seed N (repeat a scene), --autoplay, --mute, --size WxH, --shots PREFIX N (renders N screenshots of different scenes and exits), --phase-shots PREFIX (one screenshot per scene level of a song).

zenscope: see what the game hears

./build/zenscope [songs or folders...] plays a song and shows the analysis that drives the game:

  • a phase timebar with the scene levels (calm / mid / peak) and where the scene changes happen
  • the detected structure: intro, verse, build, chorus, drop, break, outro, with similar parts grouped
  • pulse zones, a 16-band spectrum, loudness, intensity and onsets, and bar lines
  • a live readout of the current section and the game's density/speed/glow profile, with the beat in the bar
  • a zoomed detail view with the beat grid
  • four beat-aligned samples cut from the song (an experiment for line clears, not used by the game yet): HIT (one clean transient, 1 beat), LOW (a bass-led half bar), TONE (a sustained, steady half bar) and HOOK (the first bar of the most repeated chorus/drop)

Space plays/pauses, Left/Right seek 5 s, Up/Down zoom the detail view, N/P change song, click to seek, 1–4 or a click on a sample button plays that sample (over the song, or alone when paused). The SPEED buttons (top right) slow the song down to ×0.5 or ×0.25, tape-style (the pitch drops too); click the active one again to go back to normal speed. The game and zenscope share the same analysis and song plan code (src/songplan.*), so what you see is what the game uses.

Controls

Keyboard and gamepad both work at the same time. A gamepad is detected at startup and on hot-plug, and the on-screen hints follow whichever device you used last. Controllers are recognised through the bundled SDL_GameControllerDB (third_party/gamecontrollerdb.txt; extra mappings can be given in SDL_GAMECONTROLLERCONFIG). A controller missing from it still works with a generic layout.

Action Keyboard Gamepad
Move ← → D-pad / left stick
Soft / hard drop ↓ / Space Down / Up
Rotate ↑ or X (Z or J to rotate left) A (B/X to rotate left)
Hold C / Shift (tap) LB / RB
Slow motion (when the gauge is full) V / E ZL / ZR (LT / RT)
New scene T Y
Next song N / Tab / Enter / PageDown Back
Seek ±10 s in the song (testing) Ctrl+Shift+Left/Right
Next level (debugging) L
Fill the bonus gauge (testing) B
Pause Esc / P (Q quits while paused) Start
Fullscreen F / F11

Slow motion and Tetris rewards

Each cleared line charges one step of the bonus gauge on the left of the board (12 steps to fill it; a Tetris charges 5). Once it's full, the slow-motion button starts the bonus: for 10 s the gauge drains, the song slides down to half speed (tape-style, so it also sounds lower) and, since gravity follows the song's beat, pieces fall half as fast. Use it to clean up the stack in a part that's too fast. The screen's colors are reversed while it lasts (swept in and out from the board), and the scene slows down too, with one animation from a pool (halo, hourglass, ripples, orbits, stasis, ribbons, clock).

A Tetris scores a 400-point bonus (800 back-to-back) and plays a celebration from another pool (pillars, blooms, spiral, waves, starfall, frame trace, light sheet); a back-to-back Tetris plays two at once.

How the music shapes the game

Each song is decoded and analyzed in the background (under 1 s):

  • Tempo and beat grid: gravity steps land on the beat, at 1 row every 2 beats, every beat, or every half beat, depending on the song's energy at that moment.
  • Levels: one level per 20 lines, up to a plateau at level 20. Each level speeds up the beat-locked gravity (at the plateau: 5, 10 or 16 rows per beat for calm, mid and peak parts, on musical subdivisions, capped at 28 rows/s) and shortens the lock delay (0.55 s → 0.35 s). Game over resets the level.
  • Key: the base hue follows the circle of fifths.
  • Brightness, bass/air balance, dynamics, density: choose the mood (night, dusk or pale), the particle layouts, bloom, how strongly things react, and the camera's motion.
  • Song structure: the song is split at bar lines into labelled segments (intro, verse, build, chorus, drop, break, outro), and segments that sound alike are grouped. Each segment eases the scene's density, speed, glow and saturation (builds ramp up, breaks thin out).
  • One identity per song: background, main particles, blocks, frame and mood stay the same for the whole song. At most three intensity levels (calm, mid, peak) shift the hue slightly and add color, glow or an extra particle layer. Changes happen only when the level changes, crossfading over 8 s, and never interrupt each other.
  • Calm by design: visuals follow slow (~1 s) envelopes of the music and there is no camera shake or flashing. Beat pulses appear only during peak sections (choruses, drops), stronger for faster songs; songs above ~110 BPM also get soft hits on strong transients there.
  • Live bands and loudness, heavily smoothed, drive particle motion and glow.

The scene seed combines the song's fingerprint with a random seed for each run, so the same song looks different every time. The combinatorial space covers 8 palette schemes × 3 moods, 24 backgrounds, 38 particle layouts × 20 particle shapes (none, one or two layers), 12 continuous surface layers (smoke, silk, lava, caustics, ink, geometry, aurora, fog, beams, flowing rings, liquid, cloud shades), 22 block materials × a continuous family of block shapes (cube → rounded → sphere → gem), 18 board frames, a rare audio equalizer (3 layouts × 6 renderings × 4 resolutions × 3 colorings) and light rays, 22 line-clear effects, 25 transition shapes, and a post-processing grade (bloom, vignette, chromatic aberration, grain, split-toning). The scene name is shown in the bottom-left corner.

Releasing

.github/workflows/wheels.yml builds wheels for Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows (x86_64) plus a source distribution on every push, and publishes them to PyPI when a v* tag is pushed (git tag v0.1.0 && git push --tags). Publishing uses PyPI trusted publishing: on pypi.org, add a publisher for project zentris, repository Gregwar/zentris, workflow wheels.yml, environment pypi.

Metadata

Release files for zentris 1.2.0

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

Source distribution (sdist)

Source distribution for zentris 1.2.0
File Size Uploaded
zentris-1.2.0.tar.gz 841.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for zentris 1.2.0
File
zentris-1.2.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
zentris-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
zentris-1.2.0-py3-none-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64, Linux glibc 2.27+ ARM64 Details
zentris-1.2.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
zentris-1.2.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 6.9 MB

Release files / zentris-1.2.0.tar.gz

Download URL zentris-1.2.0.tar.gz
Size 841.6 kB
Tags Source
SHA-256 checksum
How to use checksums
8d3fae18395b1e2a31ed21c4784f441cd74dc2c4b255fc5234d53b834515e041
BLAKE2b-256 checksum
How to use checksums
43b8da299841f8bacb069e288c0fe761f7b65db352aa727a9dff74edef6f96c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / zentris-1.2.0-py3-none-win_amd64.whl

Download URL zentris-1.2.0-py3-none-win_amd64.whl
Size 960.1 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
63964bc7518af1a538665ac1a91db034bdc3d8f01de4374a65b66ebc4cccdcf9
BLAKE2b-256 checksum
How to use checksums
8a8486b703f126f8c7d8f506235d28bb630ebbe83d1188f1305961abbcfb17bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / zentris-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL zentris-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 1.5 MB
Tags Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
2dc3bdf61542b1fbc9577ddb78394d080d6c0f7b3694ac00f2eb46e93b0a5ef9
BLAKE2b-256 checksum
How to use checksums
c54bf9abecd71d4570ae9bc50b7b5119fe0f3b12b91b3dbc792c53fde0bcd63c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / zentris-1.2.0-py3-none-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

Download URL zentris-1.2.0-py3-none-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Size 1.5 MB
Tags Linux glibc 2.27+ ARM64 Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
85e553030210ddd158b486c7e7d407d95791ed409671e3ed9340da9953935b9f
BLAKE2b-256 checksum
How to use checksums
4662cf46e9097dbb026a5ce8e0b04ce6384afedeb57033df05477c7eaec563b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / zentris-1.2.0-py3-none-macosx_11_0_x86_64.whl

Download URL zentris-1.2.0-py3-none-macosx_11_0_x86_64.whl
Size 1.1 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
961200d884f4017abc195ddc959861fb6d5f1cd1ca144b614b59d2d98e0eacbe
BLAKE2b-256 checksum
How to use checksums
49bd4962a2d1878103c559b70acb8c23fe4010a2d5ba3ea573787131b186386e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / zentris-1.2.0-py3-none-macosx_11_0_arm64.whl

Download URL zentris-1.2.0-py3-none-macosx_11_0_arm64.whl
Size 992.7 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
9e9a29fd08e111e363120ffb87b76072c89da49696416232fa4ea8a484abb939
BLAKE2b-256 checksum
How to use checksums
26cb6c2ae13a9e12787a8806e1532d16b7a4bda1b7f900c1ad5a8ce468bf12e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log
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