KQ-1579 · IMPASTO/KNIFE

Impasto Knife

A primed board and a palette knife: drag and the knife lays a slab of thick oil paint with a striated swath behind the blade, a ridge squeezed out at each end and a bead pushed ahead that stays as a lump where you lift, all lit per pixel by a low raking light with an oil gloss in one WebGL2 pass. The knife scoops up the wet paint it crosses, mixing in log reflectance so yellow and blue make green, and folds that residue into the next stroke's fresh dollop, so the board builds up like a painting. A double click (or Delete) scrapes the board: an edge sweeps across on the move ease, heaping every row's paint into a banded ridge it carries off and leaving a ghost in the gesso; the drawn knife presses on springs.flick, and arrow keys plus Enter pull strokes without a pointer.

KQ-1579 · IMPASTO/KNIFE

Arrow keys move the knife; Enter or Space pulls a stroke the way it faces; Delete scrapes the board.

Fieldline · Studio oils

board primed · drag to paint, double click to scrape

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/impasto-knife

Props

thickness
number
default 0.6
How much body the paint has, 0.2 to 1: a thin glaze to heavy slabs with tall ridges.
palette
"ochre" | "harbour" | "garden" | "ink"
default "ochre"
The pigments the knife loads, one fresh dollop per stroke in turn.
colors
string[]
default —
Two to six CSS colours that replace palette.
lightAngle
number
default 225
Where the raking light comes from, in degrees clockwise from the right edge: 225 is the top left.
lightHeight
number
default 0.3
How high the light stands, 0 (raking, long highlights) to 1 (overhead, flat).
gloss
number
default 0.5
How glossy the oil is, 0 (matte) to 1 (wet).
knifeWidth
number
default 30
The blade's width, px.
blend
number
default 0.55
How much wet paint the knife scoops up and folds into its load, 0 to 1.
ground
string
default "oklch(from var(--warn) 0.93 0.018 h)"
The primed board's colour, any CSS colour. A fixed gesso in both themes by default.
defaultStrokes
number
default 3
Knife strokes already on the board when it first shows, 0 to 4.
interactive
boolean
default true
The pointer paints.
clearKey
string | number
default —
Any change scrapes the board clean.
seed
number
default 3
Seeds the first strokes and each stroke's blade marks.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
label
string
default "Paint board"
The board's accessible name, for the keyboard's way in.
sound
boolean
default false
Play the knife's scratch on the board. Off unless asked for.
onStroke
(count: number, pigment: number) => void
default —
A stroke was laid: how many so far, and which pigment of the palette it loaded.
onClear
() => void
default —
The board was scraped clean.
disabled
boolean
default false
Nothing paints and the keyboard's way in is off.
className
string
default "h-56 w-full"
Sizes the board.