Skip to main content

pixflex

Lay out images the way you lay out a web page. Describe the arrangement as a flexbox tree, let Yoga work out every rectangle in whole pixels, then place numpy / torch / any array-API arrays into it — with as few copies as possible and no manual index shifting.

import numpy as np
from pixflex import Column, Row, Slot

with Column(width=1280, height=720, background=(16, 16, 20)) as root:
    title = Slot(height=80)
    with Row(flex=1, gap=8):
        video = Slot(flex=3, fit="contain", background=(0, 0, 0))
        plot = Slot(flex=1, fit="exact")
    footer = Slot(height=40)

plan = root.solve()  # no arrays involved
plan[plot].hw  # (600, 318): render your plot at exactly this size
img = plan.compose({title: t, video: frame, plot: p, footer: f})  # one allocation

Install

pip install pixflex                 # numpy arrays; resizing needs one of the extras below
pip install "pixflex[numpy,resize]"  # numpy + Pillow/OpenCV resizers
pip install torch                   # torch tensors need no extra

Every image here was produced by pixflex itself (Yoga solves the layout, arrays are composed into one buffer). See the full gallery with code →

The Yoga homepage layout, solved and filled Justified photo rows
Contact sheet Torch feature maps

Also: photo collage, before/after, one layout at three widths, and recreations of the Yoga docs' justify-content, align-items, flex-direction, flex-wrap, gap, position: absolute and sizing playgrounds.

Concepts

  • Declare with Column/Row/Box (containers, usable as with blocks or with children=[...]) and Slot (leaves). Style keywords are CSS-like and come from yogalayout: flex=1, gap=8, padding=(10, 20), margin_inline=10, position="absolute", "50%", ...
  • Solve: plan = root.solve() gives absolute integer Rect(y, x, h, w)s, no arrays touched.
  • Place: plan.compose({...}), or keep a reusable Canvas (below).
  • Fit per slot or per write, following CSS object-fit: contain (default), cover (crops first, so only kept pixels are resized), fill, none, scale-down, exact (raises ShapeMismatch instead of resizing). align follows object-position ("top left", or an (x, y) fraction pair).
  • Writes never change the layout and never leave their slot's rectangle.
  • Stacking follows tree order (CSS painter's algorithm): later nodes draw on top of earlier ones, whatever order you write content in. A slot absolutely positioned over another (a logo over a video) is redrawn automatically, clipped to the overlap, whenever the slot below is rewritten. (Pixels of overlaying slots are kept for this; mutate your array in place and the next redraw shows the new data.)

Addressing slots

Plans and canvases are indexed by any hashable. Recommended, in order:

  1. Handles — the Slot/Box objects themselves (identity-hashed), bundled in a NamedTuple if you build the layout in a function. Static checkers can follow them.
  2. Enum members (or tuples, ints...) via Slot(key=Panel.VIDEO); with root.solve(key_type=Panel) pyright rejects cv["video"] and typos.
  3. Strings for scripts and config-driven layouts. Unknown keys raise KeyError with a "did you mean" hint; duplicates raise DuplicateKeyError at solve().

cv.untyped / plan.untyped give a Hashable-keyed view when key types are mixed.

Reusing a canvas (video, dashboards)

cv = plan.canvas(like=header_img, static={title: header_img, footer: footer_img})
for frame in frames:
    cv[video] = frame  # only the video slot's rectangle is rewritten
    writer.write_frame(cv.array)  # live buffer, zero copy
keep.append(cv.snapshot())  # explicit copy when you need to keep a frame
  • Constant slots are drawn once. When a contain/none placement changes size, only the stale padding strips are cleared.
  • cv.clear(k), cv.update({a: x, b: y}), cv.sub(box) (a view-backed canvas with box-relative rects), cv.view(k) (no-copy view), cv.to_numpy() (the one device→host copy).
  • cv.ring(n) cycles n pre-drawn buffers for async consumers (encoder threads).
  • Leading axes: plan.canvas(like, axes="THWC") broadcasts static parts over time and writes a whole clip with one batched resize. Axes may use H W C T N in any order ("CHW", "HW"...).

Mismatched data

Mismatch Behaviour
Size ≠ slot slot's fit applies; exact raises ShapeMismatch
1 channel into C broadcasts; missing channel axis is added
RGBA into RGB raises unless alpha="drop" or "over"
dtype cast only if lossless (can_cast); else DTypeMismatch; convert=fn overrides
device / array library raises (to_device=True allows a transfer)

Backends and resizing

Allocation and writes go through array-api-compat, so numpy and torch work today (both are tested). Resizers are chosen lazily: torch → F.interpolate(antialias=True); numpy → OpenCV, else Pillow (uint8 / float32), else SciPy. Install one (pip install pixflex[resize]) or plug in your own with pixflex.register_resizer("numpy", fn). Not yet supported: immutable arrays (JAX) and cupy are untested.

Development

uv sync --group dev                      # (a venv with CPU torch installed by hand: use `uv pip install -e .` instead)
.venv/bin/python -m pytest
uv sync --group examples && python examples/video_overlay.py    # writes out/video_overlay.mp4

See docs/HANDOVER.md for the design, status and next steps.

MIT licensed.

Metadata

Release files for pixflex 0.1.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 pixflex 0.1.0
File Size Uploaded Provenance Actions
pixflex-0.1.0.tar.gz 1.2 MB With provenance Details for pixflex-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pixflex 0.1.0
File Interpreter ABI Platform Actions
pixflex-0.1.0-py3-none-any.whl Python 3 none any Details for pixflex-0.1.0-py3-none-any.whl

Total release size: 1.3 MB

Release files / pixflex-0.1.0.tar.gz

Download URL pixflex-0.1.0.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
901d26e8ca6be65f158a16bc0b82fd6d1b61e3c40b886468544e8c8fa830bda2
BLAKE2b-256 checksum
How to use checksums
990b1ad3d24542a24a7ddf3fed9aa8d293ce8d9d3f598728cb543d0fc7e8829d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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

Transparency log

Release files / pixflex-0.1.0-py3-none-any.whl

Download URL pixflex-0.1.0-py3-none-any.whl
Size 20.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3b9ad4505bbb7a1ef677d6fa7ff679560f51457092d9696ec3a77cf7edb8b914
BLAKE2b-256 checksum
How to use checksums
b0b0fbaaabc83142c760025304926dcaa4e13be075fc2e65e7620388246ba6a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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

Transparency log

Security for pixflex 0.1.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pixflex-0.1.0.tar.gz

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

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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