M64GitHub/movy
A terminal graphics engine for rendering, effects, and animation. Built for games, engines, and visual frameworks.
movy is a terminal-based graphics and animation engine that brings pixel-level rendering, visual effects, and interactivity to text mode.
movy began with a simple vision — to bring real rendering power to the terminal. It features layered drawing, alpha blending, z-index ordering, and a programmable pipeline that adds motion and color to text mode.
Using ANSI half-blocks for higher vertical resolution, movy supports transparent sprites, per-frame visual effects, and real-time composition. Visuals flow through effect chains — reusable transformations that enable transitions, layering, and post-processing.
movy drives animation through IndexAnimators (looping, bouncing, or one-shot), waveforms, and easing functions — creating smooth motion across frames, positions, and colors.
The result is a modular visual engine — expressive, composable, and built for creative experimentation.
(alien mouse move demo)
(dev snapshot of a game)
(window demo)
Check out games, demos, and tools built with movy in the Gallery below!
At its heart, movy is built on composable rendering, effect-driven visuals, and structured interaction. The system is organized around a few core types that coordinate how visuals are drawn, animated, and composed on screen.
movy's rendering engine is centered around composable surfaces and reusable visual logic.
RenderSurface is the foundational structure — a 2D matrix of pixels (with optional text overlays) that anything visual draws onto. It can be resized, cleared, and converted to ANSI via .toAnsi().
RenderEffect modifies a RenderSurface by applying visual transformations such as blur, dim, stretch, or color shifting. It receives input and output surfaces via a RenderEffectContext, which handles size awareness and expansion when needed. Internally, RenderEffect acts as an interface and wraps an effect instance to make it compatible with chaining, pipelines, and dynamic surface management.
Each effect defines its own run() method and validate() method, and can optionally declare how much space it requires beyond the surface bounds. Effects can be run manually on surfaces, or exposed through a simple asEffect() function to integrate cleanly into the rendering system. They will automatically operate with RenderEffectContext, gaining full expansion handling and chaining capabilities.
RenderEffectContext bundles an input surface, an output surface, and tracks any applied surface expansion. It allows effects and chains to dynamically resize their output to support visuals like glow or shake.
RenderEffectChain is a reusable sequence of effects, applied in order to a RenderEffectContext. It takes care of intermediate surface allocation and ensures the final output is properly expanded. It's ideal for chaining multiple post-processing steps like fade → blur → glow.
RenderObject combines a RenderEffectContext with an optional RenderEffectChain. It acts as a unit of rendering — providing a structured way to send a visual input surface through the effect system. The output surface is automatically created and kept in sync. (Conceptually, it's "a surface + maybe effects".)
RenderPipeline processes a list of RenderObjects. Each object's effect chain (if present) is run, and their results are composited using the RenderEngine. Optionally, a final post-processing chain can be applied to the merged result.
RenderEngine performs the actual surface merge. It composites multiple RenderSurfaces into a single output, applying z-ordering, pixel blending, and visibility logic. This is used by the pipeline, UI system, and manual rendering flows.
Screen holds the final output surface. Manually, it allows you to add RenderSurfaces or Sprites directly and call screen.render() to composite them using the RenderEngine. Alternatively, its output surface can be rendered by the RenderPipeline or the UI Manager. Finally, screen.output() prints the result to the terminal using ANSI escape sequences.
movy_video adds full-motion video playback to the terminal, built on FFmpeg and SDL2.
The module exposes a VideoDecoder type that manages the entire decode pipeline, from opening media files to extracting frames and audio samples. Video frames are automatically scaled and rendered to movy RenderSurface objects, allowing seamless integration with the rest of the rendering engine.
See movycat for a complete terminal video player built with movy_video.
movy_video has been tested and confirmed working with:
The module uses the modern FFmpeg channel layout API (AVChannelLayout) and is compatible with both FFmpeg 7.x and 8.x versions.
zig build -Dvideo=true
Please see the doc folder for comprehensive guides and descriptions!
You can use demos/simple_game.zig as a starter / template.
| Project | Description | Preview |
|---|---|---|
| 1ST-SHOT | Terminal bullet-hell shooter with SID audio | |
| movycat | Terminal video player | |
| Your project here? | Submit a PR! |
Create something awesome with movy and submit a PR adding your project to this table!
Whether it's a game, a demo, an effect, or a tool — if it glows in the terminal, it belongs here.
movy is a work of love and dedicated vision, still evolving rapidly.
External code contributions are paused for now, but ideas and feedback are always welcome —
see CONTRIBUTING.md for details.
Made with ❤️ and Zig