KQ-1544 · INCENSE/SCRIPT
Incense Script
A stick of incense stands at the left edge and one thread of smoke leaves its ember, rises and drifts right, its curls catching on the letters one by one so each forms out of the smoke the thread leaves there; written, the phrase lingers, then diffuses upward into haze and the next one begins. Canvas 2D draws the thread along a seeded spline with turbulence, its ageing trail, the ember's plume and every smoke particle sampled from the real glyphs, each pulled into place by a spring at springs.glide's damping ratio while a decaying swirl curls it in. A hand through the smoke pushes it aside and it drifts back, and a transparent button gives the keyboard Enter to waft a breath across the words.
Fieldline · tea rooms
Slow mornings. Quiet rooms. Tea at four.
Enter wafts a breath across the words.writing · Slow mornings · 1 of 3
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/incense-script
Props
- phrases
- string[]
- default —
- The phrases, written in smoke one after another.
- as
- "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
- default "p"
- The element that carries the phrases.
- drift
- number
- default 0.5
- How fast the thread travels and how far its wisps lean on the draught, 0 to 1.
- curl
- number
- default 0.5
- Turbulence, 0 (a laminar thread) to 1 (curling, swirling smoke).
- linger
- number
- default 2600
- How long a written phrase stays before it clears to haze, ms.
- color
- string
- default "var(--ink-2)"
- The smoke, any CSS colour; resolved on the element, so it follows the theme.
- ember
- string
- default —
- The ember, any CSS colour; a fixed glowing pigment by default.
- density
- number
- default 0.6
- How much smoke makes each letter, 0 (wisps) to 1 (dense).
- stick
- boolean
- default true
- Draw the stick of incense and its dish at the left edge.
- loop
- boolean
- default true
- Cycle the phrases; false stops on the last one, written.
- trigger
- "mount" | "inView" | "manual"
- default "inView"
- What lights the stick.
- play
- boolean
- default false
- With trigger "manual": true lights the stick, false lets it rest.
- delay
- number
- default 0
- Wait before the first thread leaves the ember, ms.
- interactive
- boolean
- default true
- A hand through the smoke pushes it aside; Enter wafts a breath across.
- dpr
- number
- default 2
- Cap on the canvas's pixel ratio (never above 2).
- onPhraseChange
- (index: number) => void
- default —
- A phrase began: its index in phrases.
- onComplete
- (index: number) => void
- default —
- A phrase finished writing: its index in phrases.
- className
- string
- default —
- Size and type; the stick and gutter are in em.
| Prop | Type | Default | Description |
|---|---|---|---|
| phrases | string[] | — | The phrases, written in smoke one after another. |
| as | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div" | "p" | The element that carries the phrases. |
| drift | number | 0.5 | How fast the thread travels and how far its wisps lean on the draught, 0 to 1. |
| curl | number | 0.5 | Turbulence, 0 (a laminar thread) to 1 (curling, swirling smoke). |
| linger | number | 2600 | How long a written phrase stays before it clears to haze, ms. |
| color | string | "var(--ink-2)" | The smoke, any CSS colour; resolved on the element, so it follows the theme. |
| ember | string | — | The ember, any CSS colour; a fixed glowing pigment by default. |
| density | number | 0.6 | How much smoke makes each letter, 0 (wisps) to 1 (dense). |
| stick | boolean | true | Draw the stick of incense and its dish at the left edge. |
| loop | boolean | true | Cycle the phrases; false stops on the last one, written. |
| trigger | "mount" | "inView" | "manual" | "inView" | What lights the stick. |
| play | boolean | false | With trigger "manual": true lights the stick, false lets it rest. |
| delay | number | 0 | Wait before the first thread leaves the ember, ms. |
| interactive | boolean | true | A hand through the smoke pushes it aside; Enter wafts a breath across. |
| dpr | number | 2 | Cap on the canvas's pixel ratio (never above 2). |
| onPhraseChange | (index: number) => void | — | A phrase began: its index in phrases. |
| onComplete | (index: number) => void | — | A phrase finished writing: its index in phrases. |
| className | string | — | Size and type; the stick and gutter are in em. |