Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanchedev/fraxel/llms.txt

Use this file to discover all available pages before exploring further.

Fraxel’s <sprite> node exposes eight visual filter props that map directly onto the browser’s Canvas 2D rendering pipeline. Most filters write to ctx.filter as a CSS-filter string (e.g. brightness(1.2) grayscale(0.5)) before each draw call; the modulate tint uses globalCompositeOperation: 'multiply' instead, blending a filled rectangle over the sprite in a separate pass. Because every filter prop accepts a reactive value — a plain number, a signal getter, or a computed function — filters update automatically frame-by-frame whenever their source signal changes, with no manual invalidation required.

Available Filters

All eight filters can be combined on a single <sprite>:
import { loadTexture } from 'fraxel/assets'

const TEX = await loadTexture('/assets/hero.png')

function Hero() {
  return (
    <sprite
      textureId={TEX}
      brightness={1.2}          // 0 = black, 1 = base, 2 = white
      grayscale={0}             // 0 = full color, 1 = fully grayscale
      modulate={[1, 0.5, 0, 1]} // RGBA tint — orange
      contrast={1.5}            // 0 = no contrast, 1 = base, 2 = double
      saturate={0.8}            // 0 = desaturated, 1 = base, 2 = double
      hueRotate={90}            // degrees
      invert={0}                // 0 = normal, 1 = inverted
      opacity={1}               // 0 = transparent, 1 = opaque
    />
  )
}
All filters are composable — you can combine as many as you need on a single <sprite> and they stack correctly. Only non-default values incur work in the render loop; each filter is skipped when its value matches the default.

Filter Reference

PropTypeDefaultRange / Effect
brightnessnumber10 = fully black · 1 = original · 2 = fully white
grayscalenumber00 = full color · 1 = fully grayscale
modulateColor[1,1,1,1]RGBA multiply tint; each channel 01 (see below)
contrastnumber10 = no contrast · 1 = original · 2 = double contrast
saturatenumber10 = fully desaturated · 1 = original · 2 = double saturation
hueRotatenumber0Degrees of hue rotation, e.g. 180 for complementary colors
invertnumber00 = normal colors · 1 = fully inverted
opacitynumber10 = fully transparent · 1 = fully opaque

Reactive Filters

Every filter prop accepts the Reactive<T> type — which means it can be a plain static value, a signal getter () => value, or any zero-argument function returning the current value. When a getter is passed, Fraxel’s fine-grained reactivity system subscribes to it and re-evaluates it each frame the signal changes, updating the sprite with zero wasted work.

Example: grayscale tied to a health signal

import { useSignal } from 'fraxel/hooks'
import { loadTexture } from 'fraxel/assets'

const HERO_TEX = await loadTexture('/assets/hero.png')

function Hero() {
  const [health, setHealth] = useSignal(100) // 0–100

  // grayscale increases as health decreases
  const grayscaleLevel = () => 1 - health() / 100

  return (
    <sprite
      textureId={HERO_TEX}
      grayscale={grayscaleLevel}
    />
  )
}

Example: brightness on hover

import { useSignal } from 'fraxel/hooks'
import { loadTexture } from 'fraxel/assets'

const BUTTON_TEX = await loadTexture('/assets/button.png')

function Button() {
  const [hovered, setHovered] = useSignal(false)

  return (
    <sprite
      textureId={BUTTON_TEX}
      brightness={() => hovered() ? 1.4 : 1}
      onPointerEnter={() => setHovered(true)}
      onPointerLeave={() => setHovered(false)}
    />
  )
}
Because brightness receives a getter function () => hovered() ? 1.4 : 1, Fraxel automatically tracks the hovered signal and re-applies the filter only when it changes — no polling, no manual updates.

The Color Type

Color is a plain TypeScript tuple [number, number, number, number] representing an RGBA color with each channel in the 01 range. It is used by Sprite.modulate and by Rectangle’s fillColor and strokeColor props.
import type { Color } from 'fraxel'

const white: Color           = [1, 1, 1, 1]
const red: Color             = [1, 0, 0, 1]
const orange: Color          = [1, 0.5, 0, 1]
const transparentBlue: Color = [0, 0, 1, 0.5]
const halfAlphaGreen: Color  = [0, 1, 0, 0.5]
The alpha channel ([3]) in modulate controls the strength of the multiply blend — 1 applies the full tint, 0 leaves the sprite unaffected. This is distinct from opacity, which controls the overall transparency of the entire sprite.

modulate vs opacity

These two props both affect how transparent or tinted a sprite appears, but they work differently:
modulateopacity
MechanismCanvas globalCompositeOperation: 'multiply' fill passctx.filter = 'opacity(n)' applied per pixel
EffectMultiplies each pixel’s RGB channels by the tint color — dark colors darken the sprite, bright colors shift its hueUniformly scales the alpha of every pixel
Use forColor tints, damage flash, elemental effectsFade in/out, ghost effects, UI overlays
Default[1, 1, 1, 1] (no change)1 (fully opaque)
modulate example — a red damage flash:
<sprite
  textureId={ENEMY_TEX}
  modulate={() => isHit() ? [1, 0, 0, 1] : [1, 1, 1, 1]}
/>
opacity example — fade out when dying:
<sprite
  textureId={ENEMY_TEX}
  opacity={() => health() / maxHealth}
/>
You can combine both on the same sprite — for instance, a semi-transparent ghost enemy with a blue tint:
<sprite
  textureId={GHOST_TEX}
  modulate={[0.5, 0.7, 1, 1]}
  opacity={0.6}
/>

Related pages: Nodes 2D · Reactivity · Hooks · Animation · Assets · API: Nodes 2D

Build docs developers (and LLMs) love