npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-gameface-rasterize

v1.0.0

Published

A Vite plugin that bakes expensive static CSS decorations into pre-rendered textures using the Gameface Player, so cohtml draws a textured quad instead of re-evaluating shadows, gradients and filters every frame.

Readme

vite-gameface-rasterize

Write normal CSS. Ship textures.

cohtml pays a per-frame cost for box-shadow, filter, gradients and rounded fills, which is why the Gameface guide tells you to avoid them. This plugin lets you use them anyway: at build time it renders each marked element in the Gameface Player itself, saves the decoration as a texture, and replaces the expensive CSS with an image the engine draws as a textured quad. Your text, children, bindings and event handlers stay live DOM.

The output is verified against the original in the engine, so "pixel-identical" is a claim the tool can actually check.

<div class="panel" data-rasterize>
    <h2>Squad status</h2>
    <p>This text is still text.</p>
</div>
// vite.config.ts
import rasterize from 'vite-gameface-rasterize';

export default defineConfig({
    plugins: [rasterize()],
});

That is the whole integration. GAMEFACE_PATH in your .env points at the Player; the plugin does the rest on vite build and leaves vite dev completely alone.

What it costs and what it buys

Measured on the included stress example - 60 cards, each a rounded box with a 0 20px 120px 30px shadow, at 1920x1080 on cohtml 3.2.0.2, with the grid invalidated every frame so the decorations genuinely re-render. Numbers are the Coherent_GPU trace event, median over ~190 frames, reproducible across runs:

| Variant | GPU p50 | GPU p95 | Paint p50 | |---|---|---|---| | live CSS | 3.277 ms | 3.284 ms | 0.177 ms | | baked | 2.182 ms | 2.193 ms | 0.149 ms |

33% less GPU time per frame, for 3.5 MB of VRAM - and all 60 cards share one texture, because identical decorations bake once. The build summary prints that trade every time, so it is never implicit:

rasterize cohtml 3.2.0.2 - scale 2x
  div-card-8608cb57  flat    1024x884    82.2 KB on disk     3.5 MB vram

  1 asset  3.5 MB vram of 32.0 MB budget  0 cached, 60.1s

Note that box-shadow on a static page is close to free on this engine - cohtml repaints dirty regions only, so a shadow nothing disturbs is not redrawn. The win appears exactly where you would expect it: when the UI moves.

Modes

| Mode | What it bakes | When | |---|---|---| | slice | Decoration captured at a canonical size, emitted as a 9-slice | Any element whose size is decided at runtime | | flat | Decoration captured at the element's own size, one image | Fixed-size elements | | element | An entire subtree flattened into one image, with dynamic and interactable parts kept live and pinned on top | Nameplates, cards, HUD panels - deep static wrapper trees | | auto | flat if the CSS declares a fixed size, otherwise slice | The default |

Element mode is the one that changes the shape of your DOM: in the demo, a nameplate's wrapper hierarchy collapses to one image plus the three nodes that actually needed to stay live - the bound name, the HP fill, and the report button.

Every build checks itself

The failure modes that matter here are silent: a texture that never reached the DOM, one that landed in the wrong place, one that is a picture of nothing. All three render as a page that loads. So every build ends by rendering itself twice - as shipped, and with the generated tags stripped out - and writing both plus their difference to rz/report/:

  index.html  16/17 textured  0.11% of pixels differ  rz/report/index.html.png

Three panels, keyed by a colour stripe: green is live CSS, blue is what shipped, red marks where they disagree. Two extra page loads, and it is the only check that sees the page whole.

Alongside it, three assertions that need no extra work:

  • every marked element resolved to a texture - counted in the built page, in the engine, with each miss named and explained ("its key is shared by 5 bakes and none of their ancestor classes matched")
  • no asset is a picture of nothing - an empty capture is refused rather than shipped, so the element keeps the live CSS that was working
  • no two assets are the same image - and the message names which part of the bake key split them

Verification

Every build already renders the finished page against itself-without-the-bake and writes both plus their difference to rz/report/. That is the check that matters, it is on by default, and it sees the page as a whole.

npx gameface-rasterize verify dist --json rz/verify.json

verify is a stricter per-asset SSIM check for CI. It currently cannot check decoration assets

  • it isolates each one by toggling a node, and a decoration is now a ::before - so it refuses to run when any are present rather than reporting a pass it did not earn.

Dev tells you what the build would do

vite dev still renders live CSS untouched. It also injects an overlay - press F9 - that classifies every marked element the way the build would, in the page, with no Player launch and no bake:

  • green shares a texture, and with how many others
  • amber gets its own, and what differs from its lookalikes
  • red will not match at runtime, and why
  • grey has nothing worth baking

It updates as you edit, so "will these forty nameplates share one texture" is answered while you are in the CSS rather than two minutes later. __rzOverlay.report() returns the same data.

Commands

npx gameface-rasterize bake dist        # capture and write textures into a built output
npx gameface-rasterize check dist       # plan and report only; no captures, no writes
npx gameface-rasterize check --url http://localhost:5173   # same, against a running dev server
npx gameface-rasterize verify dist      # per-asset SSIM; element mode only, see above
npx gameface-rasterize measure dist     # GPU time per frame, live against baked

Measuring it

Frame time cannot see this plugin. The Player paces frames at a fixed cap, so an empty page and a page full of blurred shadows both read ~31 ms, and the natural measurement returns "no change" for any page. gameface-rasterize measure reads the engine's own Coherent_GPU trace instead, alternates which variant runs first, repeats, and reports the spread:

live CSS  GPU p50 3.29 ms +/- 0.01    baked  GPU p50 2.19 ms +/- 0.00   -33.4% (n=4)

Documentation

Examples

  • examples/demo - one page covering every mode and every interesting diagnostic
  • examples/regressions - the shapes that used to fail silently: variants selected through an ancestor, identical decorations under per-element transforms, an element transparent until the game shows it, a spinning one, one that genuinely cannot be captured, and a class that arrives after mount
  • examples/stress - the benchmark behind the numbers above (node generate.mjs 60, bake, then node measure.mjs <dist>)