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