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

maalata

v1.1.4

Published

2002 era retro canvas experience — latency pipeline + CRT post-processing over canvas-ultrafast

Readme

maalata

2002-era retro canvas experience — historically-calibrated latency pipeline and CRT post-processing over canvas-ultrafast.

Why

When porting classic Flash and Shockwave codebases to HTML5, the animations become too fluid and responsive. The original experience ran at 4–8 FPS on 50/60Hz displays with real USB polling delays, OS scheduling jitter, and slow LCD panels between the user's click and the photon hitting their eye. That entire pipeline contributed to the feel. Removing it makes the port feel wrong, even when every sprite is pixel-perfect.

maalata restores the authentic click-to-photon latency of a 2002 desktop — USB 1.1 polling, Windows XP scheduling, 8 FPS application loops, TN panel response — and layers CRT post-processing on top. The limitation is strictly visual: the browser continues to process interaction at native refresh rates.

How it works

Click-to-photon latency pipeline

Four discrete stages model the full path from input device to screen, each with historically-accurate timing:

  click/keypress
       │
       ▼
 ┌───────────┐
 │ USB Poll   │  8ms fixed       USB 1.1 HID host controller polls
 │ (125 Hz)   │                  at 125 Hz — reads whatever input
 └─────┬─────┘                  reports accumulated since last poll
       │
       ▼
 ┌───────────┐
 │ OS Kernel  │  10ms fixed      Windows XP ISR → DPC → thread
 │            │                  scheduling on a loaded system
 └─────┬─────┘
       │
       ▼
 ┌───────────┐
 │ App Frame  │  125ms collect   Game loop processes all pending
 │ (8 FPS)    │                  input once per frame tick —
 └─────┬─────┘                  0–125ms phase jitter (avg 62.5ms)
       │
       ▼
 ┌───────────┐
 │ LCD Panel  │  25ms fixed      2002 TN panel pixel transition
 │            │                  (Dell 1504FP rated 25ms)
 └─────┬─────┘
       │
       ▼
    photon

Worst-case: 168ms (8 + 10 + 125 + 25). Average: ~119ms. GPU queuing latency is handled by canvas-ultrafast's real WebGL triple-buffer FBOs rather than a simulated delay stage.

Pixel art smoothing (ScaleFX + sharpsmoother + marching squares + EWA smooth)

maalata targets 1:1 pixel art — consumers draw at native resolution and get improved visuals automatically, no code changes needed. The library handles all internal upscaling and smoothing transparently.

Pixel-perfect rendering — maalata is designed for pixel art canvases. All WebGL textures across canvas-ultrafast and maalata use gl.NEAREST (nearest-neighbor) filtering — no hardware bilinear interpolation at any stage. The canvas element uses CSS image-rendering: pixelated. Together with canvas-ultrafast's imageSmoothingEnabled: false default, this eliminates smoothing from WebGL texture sampling through browser compositing. The WebGL output is always the same size as the canvas input; all internal upscaling is purely in GPU FBOs.

An eight-pass pre-processing pipeline smooths pixel art edges using ScaleFX (Sp00kyFox), sharpsmoother (guest(r)), and marching squares contour AA, producing SVG-quality smoothing for pixel art. ScaleFX performs 6-level edge classification with precise slope detection using Compuphase perceptual color distance, outputting at 3× scale. Sharpsmoother adds edge-preserving color blending. Marching squares provides contour-based edge anti-aliasing using signed distance fields — classifies 2×2 original pixel cells, computes contour positions, and blends via smoothstep. An EWA smooth downsample (raised-cosine polar filter, no negative lobes) reduces back to native resolution with maximum smoothness and zero ringing. Same output resolution as input, completely smooth edges with no visible staircase artifacts. Purely algorithmic — no lookup tables or async loading needed.

The smoothing pipeline is implemented as a standalone SmoothingDisplay class that can be used in two modes, both configurable at init and toggleable at runtime:

  • With CRT (crt: true, smoothing: true): CRTDisplay delegates to SmoothingDisplay for passes 0-7, then applies CRT effects on the smoothed output.
  • Standalone (crt: false, smoothing: true): SmoothingDisplay runs its own RAF loop and blits smoothed output directly to screen — pixel art edge smoothing without the retro CRT look.

On a real 2002 CRT, pixel art was displayed at native resolution and the analog beam naturally softened edges — the pre-upscaling is an artifact of the modern web canvas that this pipeline corrects.

Ready Texture (raw pixel art, sRGB, W × H)
    |
[Pass 0: ScaleFX metric]
    |  Compuphase perceptual color distance to 4 neighbors (A,B,C,F)
    |  Output: RGBA16F distance vector (W×H → W×H)
    |
[Pass 1: ScaleFX strength]
    |  Corner interpolation strength via edge/threshold comparison
    |  Reads pass 0 metric, output: RGBA16F (W×H → W×H)
    |
[Pass 2: ScaleFX ambiguity]
    |  Dominance voting, single-pixel detection, edge orientation
    |  Reads pass 0 metric + pass 1 strength
    |  Packs: (res + 2*hori + 4*vert + 8*orient) / 15 (W×H → W×H)
    |
[Pass 3: ScaleFX edge level]
    |  6-level edge classification (±3 texels), subpixel tag assignment
    |  Packs: (crn + 9*mid) / 80 (W×H → W×H)
    |
[Pass 4: ScaleFX 3× output]
    |  Decode tags → map 3×3 subpixel grid → fetch original pixel color
    |  Reads pass 3 edge level + original input (W×H → 3W×3H)
    |
[Pass 5: Sharpsmoother]
    |  3×3 perceptual-weighted edge-preserving smoothing (3W×3H → 3W×3H)
    |
[Pass 6: Marching squares]
    |  Contour-based edge AA: 2×2 cell classification, SDF blending
    |  Reads pass 5 + original input, NEAREST only (3W×3H → 3W×3H)
    |
[Pass 7: EWA smooth downsample]
    |  Raised-cosine 8×8 polar downsample, no negative lobes
    |  SUPPORT=1.5, u_downscaleFactor=3.0 (3W×3H → W×H)
    |
Smoothed Texture (anti-aliased edges, sRGB, W × H)
    |
[Pass 8: CRT shader -> Screen]

Total VRAM for the smoothing pipeline is 25 WH (2× RGBA16F W×H + 2× RGBA8 W×H + 2× RGBA8 3W×3H + 1× RGBA8 W×H). Requires EXT_color_buffer_float WebGL2 extension (99%+ support). All textures use NEAREST filtering exclusively.

The EWA smooth downsample uses a raised-cosine envelope with polar distance — no negative lobes means zero ringing and maximum smoothness. 8×8 grid (64 taps), SUPPORT=1.5 output pixel radius. At u_downscaleFactor=3.0 (main pipeline): halfScale=1.5, support extends to 2.25 source texels from center, ~32 of 64 samples contribute. At u_downscaleFactor=1.5 (for screenshotUpscaled() 3×→2×): halfScale=0.75, ~16 of 64 samples contribute. The kernel adapts automatically via u_downscaleFactor.

CRT post-processing

A single combined GLSL fragment shader applies 12 effect stages in an optimized order — all UV modifications happen before any texture reads:

  1. Barrel distortion + curvature — screen warp with OOB early-out
  2. Vertical jitter — UV offset (conditional)
  3. Horizontal tearing — UV offset (conditional)
  4. Texture sampling — 4-way branch: BFI x aberration (1/3/3/9 reads)
  5. CRT gamma decode — linearize with γ=2.4 (BT.1886)
  6. Static noise — time-seeded hash for animated grain
  7. Glow/bloom — smoothstep-based (no extra texture reads)
  8. Signal loss — scanline-frequency intensity modulation
  9. Lighting mask — flicker + vignette in a single multiply
  10. Pixel beam — 2D Gaussian CRT phosphor dot simulation (brightness-dependent width)
  11. sRGB gamma encode — re-encode with γ=2.2 for display
  12. Color — desaturation, contrast, brightness (perceptual space)

Every effect block is guarded by a > 0.0001 threshold check for early-out when disabled. The shader was combined from three MIT-licensed sources — see Inspiration & prior art for full attribution.

Pixel beam — Each virtual CRT pixel is rendered as a 2D Gaussian phosphor dot with brightness-dependent width, simulating the electron beam's cross-section as it excites phosphors. Brighter pixels have wider beams (higher current spreads the electron beam), creating natural per-pixel bloom. The beam's vertical Gaussian profile creates scanline-like gaps between rows — on real CRTs this was the same physical effect as the horizontal dot shaping, not a separate phenomenon. The virtual CRT pixel grid is auto-derived from canvas dimensions, targeting ~3+ canvas pixels per CRT dot for visible roundness across 144p–720p. Inspired by CRT-Geom (cgwg) beam profile and CRT-Royale brightness-dependent sigma.

Colorspace pipeline — The shader simulates a 2002-era PC CRT monitor on a modern sRGB display. Input is sRGB-encoded (from Canvas 2D API via WebGL RGBA textures — no hardware sRGB conversion). The shader decodes with CRT gamma (γ=2.4, BT.1886 standard for CRT phosphor response), processes physical effects in linear space, then re-encodes with sRGB gamma (γ=2.2). The net gamma of 2.4/2.2 ≈ 1.09 produces the subtle contrast boost characteristic of CRT viewing — midtones render slightly darker, matching what users experienced on real CRT monitors in 2002. No color primary conversion is needed: PC CRT P22 phosphors had primaries nearly identical to sRGB/Rec.709 (unlike TV NTSC/PAL standards which require matrix conversion).

Black Frame Insertion (BFI) — On displays running at 120Hz+, a rolling scan simulates CRT phosphor decay using a 3-frame trailing buffer. Hz detection uses an EMA-smoothed requestAnimationFrame delta with hysteresis (activate at 120Hz, deactivate below 110Hz). Per-channel overlap intervals and gamma-correct blending prevent banding artifacts. BFI has the highest priority in the renderer stack — CRTDisplay owns the RAF loop whenever CRT is enabled, ensuring BFI Hz detection, frame capture, and rolling scan operate at full RAF cadence unaffected by smoothing state or other feature toggles.

Feature toggles

All features are independently toggleable via RendererConfig (initial state) and runtime methods (setSmoothing(), setCRT(), updateCRTConfig()).

| Feature | Config | Runtime | Notes | |---------|--------|---------|-------| | Pixel art smoothing | smoothing: true | setSmoothing(bool) | Lazy-created on first enable | | CRT post-processing | crt: true | setCRT(bool) | Lazy-created on first enable | | Individual CRT effects | crtConfig: { ... } | updateCRTConfig({ ... }) | Early-out when ≤ 0.0001 | | BFI | crtConfig: { bfiStrength } | updateCRTConfig({ bfiStrength }) | Highest priority, auto Hz detection |

Exactly one requestAnimationFrame loop runs at any time. CRTDisplay owns the loop when CRT is enabled (calling smoothing synchronously); SmoothingDisplay owns it when CRT is off; UltrafastRenderer passthrough runs when both are off.

API overview

import { CanvasRenderer } from 'maalata';

// Full experience (default) — smoothing + CRT + BFI
const renderer = new CanvasRenderer({
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  crt: true,
  smoothing: true,
  crtConfig: {
    chromaticAberration: 0.0005,
    flicker: 0.02,
  },
});

// CRT only (no smoothing) — raw pixels through CRT shader
const crtRenderer = new CanvasRenderer({
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  crt: true,
  smoothing: false,
});

// Smoothing only (no CRT) — anti-aliased edges, no retro effects
const smoothRenderer = new CanvasRenderer({
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  crt: false,
  smoothing: true,
});

// Passthrough — raw pixel art, no processing
const rawRenderer = new CanvasRenderer({
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  crt: false,
  smoothing: false,
});

// Runtime toggling
renderer.setSmoothing(false);  // disable smoothing
renderer.setCRT(false);        // disable CRT
renderer.setSmoothing(true);   // re-enable smoothing

// Canvas 2D-compatible drawing API (provided by canvas-ultrafast)
const ctx = renderer.getCanvasAPI();

ctx.fillStyle = '#1a1a2a';
ctx.fillRect(0, 0, 640, 480);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 20px monospace';
ctx.fillText('Hello from 2002', 20, 35);

// Events
renderer.on('ready', () => { /* initial render */ });
renderer.on('suspending', ({ done }) => { /* idle shutdown (when CRT disabled) — clean up, call done() */ });
renderer.on('resuming', () => { /* transparent restart on next interaction */ });

// Cleanup
renderer.destroy();

Key exports

| Export | Role | |---|---| | CanvasRenderer | Latency pipeline + CRT display + idle lifecycle | | CanvasAPI | Canvas 2D-compatible command recording (re-exported from canvas-ultrafast) | | CRTConfig | All CRT shader parameters (barrel, pixel beam, BFI, etc.) | | RendererConfig | Constructor options (canvas, crt toggle, smoothing toggle, CRT config). Both crt and smoothing default to true and are independently toggleable at runtime. | | RendererEvent | Union type for lifecycle events |

CanvasRenderer methods

| Method | Description | |---|---| | getCanvasAPI() | Return the CanvasAPI drawing interface | | getCanvas() | Return the active <canvas> element | | getCanvasSize() | Return { width, height } | | on(event, callback) | Subscribe to lifecycle events; returns unsubscribe function | | setSmoothing(enabled) | Toggle pixel art smoothing at runtime | | setCRT(enabled) | Toggle CRT post-processing at runtime | | screenshot() | Capture current CRT-processed frame as ImageBitmap | | screenshotUpscaled() | Capture ScaleFX+AA upscaled texture as GPU-downsampled 2× ImageBitmap, or null if smoothing is disabled | | ready() | Promise<void> that resolves when the renderer is initialized | | destroy() | Release all WebGL resources and detach listeners |

Origin & name

maalata is developed for HabboWidgets. Existing rendering libraries didn't fit this highly specific use case, and extracting it into a standalone library prevents code duplication across retro-style web projects.

Maalata is Finnish for "to paint" — a homage to the Finnish roots of the early 2000s web-game scene (Habbo Hotel) that inspired this project.

Inspiration & prior art

Latency pipeline

The click-to-photon pipeline stages are derived from real hardware measurements and specifications of the era:

  • Dan LuuInput lag measurements on an iMac G4 (2002), providing the reference framework for total click-to-photon latency
  • USB 1.1 HID specification — 125Hz default polling rate (8ms intervals)
  • Windows XP scheduling — ISR/DPC interrupt handling and thread scheduling latency
  • Dell 1504FP — 2002 TN panel rated at 25ms pixel response time

CRT shader

The combined fragment shader draws from three MIT-licensed implementations:

  • Ichiaka/CRTFilter (MIT) — Original basis for the effects pipeline: barrel distortion, chromatic aberration, static noise, horizontal tearing, glow/bloom, vertical jitter, signal loss, scanlines, dot mask, desaturation, contrast/brightness, flicker
  • gingerbeardman/webgl-crt-shader (MIT) — Performance optimizations: early-out guards, OOB check after barrel distortion, Chebyshev-distance vignette, highp precision selection, combined lighting mask, configurable scanline count
  • Blur Busters CRT Beam Simulator (MIT) — By Mark Rejhon and Timothy Lottes. Rolling scan BFI with phosphor decay and variable per-pixel MPRT. Adapted: 3-frame trailing buffer, interval overlap formula, gamma-correct operations, gain-vs-blur tradeoff, per-channel independent processing

Pixel art smoothing

  • Sp00kyFox (2016-2017) — ScaleFX edge interpolation specialized in pixel art (MIT): 6-level edge classification, Compuphase perceptual color distance, subpixel tag assignment
  • guest(r) (2005-2017) — Sharpsmoother edge-preserving color smoothing (GPL v2+)
  • libretro/glsl-shaders — GLSL reference implementations: ScaleFX (MIT), sharpsmoother (GPL v2+)
  • CompuphasePerceptual color distance metric used by ScaleFX and marching squares

Rendering backend

  • canvas-ultrafast — WebGL2-accelerated Canvas 2D engine providing triple-buffered FBOs, command recording, and the ready texture that maalata's CRT shader reads from

License

AGPL-3.0-only