← All libraries

CandyMosaic

🍬 CandyMosaic

Image-to-cell renderer

port of x/mosaic image sixel kitty iterm2 ext-gd

Image-to-cell renderer for the terminal. PNG/JPEG/static GIF decoded via ext-gd and rendered via the best available protocol: Sixel, Kitty graphics, iTerm2 inline images, Unicode half-block, or quarter-block fallback.

Install

composer require sugarcraft/candy-mosaic

Quickstart

use SugarCraft\Mosaic\Mosaic;
use SugarCraft\Mosaic\ImageSource;

$mosaic = Mosaic::halfBlock();
$image  = ImageSource::fromFile('cat.png');
$ansi   = $mosaic->render($image, width: 40, height: 20);
echo $ansi;

API

use SugarCraft\Mosaic\Renderer\QuarterBlockRenderer;

// Probe terminal once, pick best protocol
$mosaic = Mosaic::probe();

// Force a specific backend
$mosaic = Mosaic::sixel();
$mosaic = Mosaic::kitty();
$mosaic = Mosaic::iterm2();
$mosaic = Mosaic::halfBlock();

// Render — returns ANSI bytes
$ansi = $mosaic->render($image, width: 40, height: 20);

// Builder for fine-grained control
$mosaic = Mosaic::builder()
    ->withRenderer(new QuarterBlockRenderer())
    ->withResize(width: 40, height: 20)
    ->build();

What's in the box

Sixel rendererxterm, foot, mlterm, wezterm, contour support.
Kitty graphics protocolkitty, ghostty, wezterm support.
iTerm2 inline imagesOSC 1337 support for iTerm2, wezterm, mintty.
Unicode half-blockUniversal fallback using ▀ + 24-bit fg/bg.
Unicode quarter-blockHigher fidelity using ░▒▓█ 2×2 sub-pixel rendering.
ext-gd integrationPNG, JPEG, static GIF via GD (imagecreatefrompng, imagecreatefromjpeg, imagecreatefromgif).
Protocol detectionMosaic::probe() checks environment variables and DA1 capability query. Cached per-process.
Kitty virtual-image placementTransmit once (a=T), then place at multiple offsets (a=p) to reduce bandwidth. Use KittyOptions::transmit() then KittyOptions::place().
Kitty zlib compressionCompress PNG payload with zlib (f=1) before base64-encoding via KittyOptions::withCompression(1). Useful for large images.
Half-block transparencyTransparent pixels (alpha=127) emit no SGR codes — terminal default background shows through. Enables overlay compositing on existing terminal content.
Sixel 256-color fallbackPass maxColors (1–256) to SixelRenderer constructor to limit palette size for terminals with limited truecolor support.

Source & demos

Try the quickstart →

Key Classes

ClassMethodDescription
Mosaicprobe()Auto-detect best terminal protocol
Mosaicsixel(), kitty(), iterm2(), halfBlock()Force specific renderer
Mosaicrender(image, width, height)Render image to ANSI string
Mosaicbuilder()Create builder for fine-grained control
Rendererdelete(imageId)Remove a previously rendered image (Kitty=APC delete; iTerm2=OSC 1337 Pop; others=empty)
KittyRendererrenderWithOptions(image, width, height, KittyOptions)Render with Kitty protocol options (virtual-image a=p, compression f=1)
KittyOptionstransmit(id), place(id, x, y)Factory: transmit inline or place a previously transmitted virtual image
KittyOptionswithZIndex(z), withCompression(f), withUseVirtual(bool)Builder: z-index stacking, zlib compression (f=1), virtual-image mode (a=p)
QuarterBlockRenderernew2×2 sub-pixel renderer via Mosaic::builder()->withRenderer(new QuarterBlockRenderer())
HalfBlockRenderernew, supportsAlpha()Unicode ▀ renderer with transparent-pixel handling (null alpha skips SGR codes, letting terminal default show through). supportsAlpha() returns false — transparency is rendered by omitting SGR, not blending.
SixelRenderernew(dither, maxColors), maxColors(), dither()DEC sixel renderer with median-cut quantizer (up to 256 colors). Constructor accepts maxColors (1–256, default 256) to limit palette size for limited-truecolor terminals. Dither enum: FloydSteinberg, Stucki, Atkinson, None.
ImageSourcefromFile(path)Load image from file
ImageSourcefromGd(image)Create from GD resource

Demos.

VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.

Inline Image

Inline Image

Renders a PNG to the terminal via the best available protocol.