Performance
Performance is something we treat as part of the brand, not as polish. A pixel-art editor that lags while you paint breaks the most basic promise it makes to its users. This page documents what Hexcalibur actually does on a typical modern laptop, how that’s measured, and how the editor stays responsive at scale.
The numbers below come from an automated benchmark suite we rerun throughout development, so they aren’t marketing claims - they’re measurements we hold ourselves to.
What you can expect while painting
Section titled “What you can expect while painting”These are the operations you feel as a user every time you draw.
| Operation | Time on a typical laptop |
|---|---|
| Paint a single stroke on a 16×16 canvas | under 0.1 ms |
| Paint a single stroke on a 256×256 canvas | ~0.2 ms |
| Paint a single stroke on a 1024×1024 canvas | ~0.7 ms |
“Stroke” here means roughly 10,000 grid cells of Bresenham line work
- well beyond what a single mouse-drag actually touches. The CPU side of painting fits well inside one display frame (~16 ms at 60 Hz), which is what keeps the brush feeling glued to your cursor even at the largest canvas sizes Hexcalibur supports.
Palette operations
Section titled “Palette operations”Palette-side work is what you feel when you reduce an imported image to a custom palette, or when a pack-level palette change cascades through every asset.
| Operation | Time on a typical laptop |
|---|---|
| Median-cut quantize a 1024×1024 image to 16 colors | ~28 ms |
| Re-quantize a 30-asset pack to a shared 16-color palette | ~57 ms |
The pack-wide re-quantize uses a small precomputed lookup table so the per-pixel work is a single array read instead of a linear scan through every palette entry. That’s what keeps it under a tenth of a second even when the operation touches 120,000 pixels across the whole pack.
Export
Section titled “Export”The numbers below cover the shelf-fit packer that lays out an atlas when you export a generic atlas or a multi-asset pack. PNG encode and ZIP construction are linear in pixel count and depend on the browser’s built-in encoder, so they’re not separately benchmarked.
| Operation | Time on a typical laptop |
|---|---|
| Shelf-fit pack 256 uniform 32×32 tiles | ~12 µs |
| Shelf-fit pack 256 mixed-size tiles (16/32/64/128 px) | ~23 µs |
In practical terms, the packer is a rounding error inside the overall export time. The bottleneck for a large export is the PNG encoding work the browser does after packing, which scales linearly with the total pixel count of the atlas.
Why it feels this way
Section titled “Why it feels this way”Hexcalibur composites layers on the GPU with a WebGL2 fragment shader. Each layer in the asset is a slice of a 2D texture array; the shader walks the active layers, applies per-layer opacity and blend mode, and produces a final pixel in one pass. This means:
- Adding more layers adds GPU work that’s effectively free at the layer counts Hexcalibur supports (32 visible layers per asset). A 16-layer composite isn’t measurably slower than a 4-layer one during normal painting.
- Changing layer opacity, visibility, or blend mode triggers a single shader pass, not a full re-render of the layer stack from scratch.
- Paint strokes write into a CPU buffer and re-upload only the changed layer texture. The compositor doesn’t care that the stroke happened; it just sees one updated texture slice next frame.
The 3D preview hangs off the same per-face buffers as the editor canvas. When you paint a face in 2D, the 3D preview updates within the same frame because the texture upload is shared.
How the benchmarks work
Section titled “How the benchmarks work”The suite measures the same code that ships in the editor - not simplified stand-ins. It covers:
- Painting - the stroke routines behind the brush and line tools (Bresenham line work plus the brush mask)
- Quantize - the median-cut color reduction used when you quantize an image to a palette
- Re-quantize - the palette-snap lookup used when a pack-wide palette change cascades through every asset
- Export packer - the shelf-fit layout that arranges tiles into an atlas at export time
There’s also a layer-composite benchmark that runs a CPU port of the GPU shader’s math. It exists purely to catch regressions in the blend logic - it doesn’t reflect what the GPU does for end users, which is much faster.
Each benchmark discards warm-up runs and reports the median across multiple measured runs, and short-running benchmarks are batched so each sample is long enough to time reliably.
The suite also runs automatically on every change to the editor, and fails the build if anything comes in more than 25% slower than the recorded baseline - so a performance regression gets caught before it ever reaches you.
Caveats and honest framing
Section titled “Caveats and honest framing”A few things this page deliberately doesn’t claim:
- These are not your numbers. Our reference laptop is one specific configuration. Your machine, your browser, your GPU, your operating system, and your background process load will all shift these numbers. The shape (sub-millisecond painting, sub-100ms palette ops) holds across the hardware we’ve tested, but the exact figures will vary.
- The composite bench is a regression-detection tool, not a user-facing budget. It runs on the CPU; users run the GPU shader. The GPU is faster than the CPU port by roughly an order of magnitude. If you instrument the editor with the browser’s performance profiler, you’ll see paint-to-screen times that are much smaller than the composite numbers above.
- We don’t publish comparisons against other editors. Different tools make different trade-offs, and our benchmarks are tuned to measure our own production code paths. We’d rather show you what Hexcalibur does and let you decide what that means for your workflow.
If you’d like to discuss a workload Hexcalibur doesn’t handle as well as you’d expect, we’d love to hear about it - reach out through the contact form on axidus.io.