remocn logoremocn

Brush Stroke Simulator

A simulated fingertip drags a blur brush across an image, revealing a pixelated overlay along its trail.

A semi-transparent fingertip glides across the frame along a chain of cubic-bezier waypoints, leaving a pixelated overlay in its wake. The reveal is driven by an SVG <mask> whose stroked <path> is rebuilt every frame from the cursor's accumulated trail — that's why the brush stroke feels continuous instead of stamping discrete dots. The cursor scales down slightly while it is "pressed" against the surface and springs back when it lifts between regions.

Installation

$ pnpm dlx shadcn@latest add @remocn/brush-stroke-simulator

Usage

// src/Root.tsx
import { Composition } from "remotion";
import { BrushStrokeSimulator } from "@/components/remocn/brush-stroke-simulator";

const BrushScene = () => (
  <BrushStrokeSimulator
    brushSize={70}
    sweepDuration={150}
  />
);

export const RemotionRoot = () => (
  <Composition
    id="BrushStrokeSimulator"
    component={BrushScene}
    durationInFrames={180}
    fps={30}
    width={1280}
    height={720}
  />
);

Props

PropTypeDefaultDescription
brushSize
number70Diameter of the cursor and width of the reveal stroke in pixels.
cursorColor
string"rgba(255,255,255,0.45)"Fill color of the semi-transparent fingertip circle.
background
string"#0a0a0a"Page background behind the simulated portrait.
baseColorA
string"#f4a261"Highlight tint of the simulated portrait beneath the overlay.
baseColorB
string"#e76f51"Shadow tint of the simulated portrait beneath the overlay.
overlayColor
string"#1f1f23"Base color of the pixelated overlay that the brush reveals.
startFrame
number12Frame at which the brushing motion begins. Earlier frames hold the unbrushed scene.
sweepDuration
number150How many frames the brush takes to traverse the full waypoint chain.
speed
number1Playback speed multiplier applied to the internal frame counter.
className
stringOptional className passed to the root container.

Notes

Mask, not clip-path

SVG <clipPath> ignores stroke-width, so a stroked path can't be used as a clip. The reveal is built with an SVG <mask> instead — white background, black stroked trail — applied to the pixelated overlay rectangle. The black stroke punches a hole through which the sharp base layer shows.

Trail length grows with progress

The mask path is rebuilt from the cursor's accumulated samples every frame. At default settings that's roughly 110 sample points by the end of the sweep — fast enough, but if you crank sweepDuration past 240 frames you'll want to reduce the per-segment sample count inside sampleBrushPath.