threadtext

Text,
embroidered.

npm ↗
GitHub ↗
TypeScriptZero dependenciesReact + Vanilla JS

Give it a word in any loaded font and it renders as raised satin floss on a transparent ground — threads that run across each stroke and fan around the curves, lifted into 3D and sewn in one satin stitch at a time. Drop it over any background. Photorealistic by construction, from the font’s real glyph geometry — not a raster filter, not an AI image.

Live demo — type a word

Size90%
Weight680
Sew rate140/s
Sew-in
Sheen

Click the artwork and type — the word re-fits to the width as you go, and colour, font, and size change live without re-stitching. Toggle Sew-in and hit Replay to watch it embroider one satin row at a time.

How it works

Glyphs drive the flow

The word is rasterised, then an exact distance transform gives a flow field — smoothed in double-angle orientation space so opposite edge-normals reinforce. Threads run across each stroke and fan cleanly around the curves, with no moiré.

Thread, lit in 3D

Thousands of pre-shaded thread sprites are laid along the flow and lifted into 3D by a dome-shade normal map, so each strand catches the light along its length. The floss sits on a transparent ground — drop it over any background — and a cursor-following sheen turns the threads over as you move.

Sewn in, one row at a time

A breadth-first pass over the stitch graph reveals the word one satin cross-row at a time — the needle following each stroke, letters filling left to right. Typing re-embroiders and animates only the newly-added letters.

Accessibility & motion

The canvas is decorative; the word is exposed to assistive tech via role="img" and an aria-label. prefers-reduced-motion: reduce is honoured — the sew-in is skipped and the word is drawn instantly.

Usage

Drop-in component

import { ThreadText } from '@liiift-studio/threadtext'

<ThreadText text="Thread" font='"Your Font", serif' weight={680} />

Hook

import { useThreadText } from '@liiift-studio/threadtext'

const ref = useThreadText({ text: 'Thread', font: '"Your Font", serif', weight: 680 })
<div ref={ref} />

Vanilla JS

import { createThreadText } from '@liiift-studio/threadtext'

// Load the face first (any @font-face / next/font / CSS Font Loading API), then:
const thread = createThreadText(document.getElementById('host'), {
  text: 'Thread', font: '"Your Font", serif', weight: 680,
})

thread.setText('Threads') // appended letters sew in; unrelated text re-sews
thread.replay()           // re-run the full sew-in
thread.destroy()          // cancel rAF, remove listeners, free canvases

Options

ThreadText options reference
OptionDefaultDescription
textThe word (or short phrase) to embroider. Required.
font'Georgia, serif'CSS font-family of an already-loaded font. The glyph geometry drives the stitch flow — load it however you like before calling.
weight680Numeric font weight (100–900).
threadColor'#fffbf3'Floss colour — the lit crest of each thread. Hex or rgb(). Changes live, no re-stitch.
fill0.9Fraction of the container width the word spans — its size. The word re-fits to width (with the remainder as padding) on load and resize.
pitchautoThread spacing in px. Smaller = finer, denser stitching (and more work). Derived from the fitted height if unset.
sewRate110Satin cross-rows laid per second during the sew-in.
sheentrueCursor-following radial sheen on the overlay canvas.
animatetruePlay the sew-in on mount and on replay(). When false (or reduced-motion), the word is drawn instantly.
editablefalseMake the surface focusable and typeable — click to focus, type to edit, Backspace to delete, Enter to replay. Shows a caret; pair with onTextChange.
reducedMotionautoForce reduced-motion. Auto-detected from prefers-reduced-motion if omitted.

no-code

Use it in Webflow & Framer

The same effect, no build step — drop it straight into your design tool.

Webflow

One script tag, then mark any element with data-threadtext. Configure it with data-* attributes.

<!-- Site SettingsCustom CodeFooter, or an Embed element -->
<script src="https://cdn.jsdelivr.net/npm/@liiift-studio/threadtext/dist/threadtext.webflow.min.js"></script>

<!-- Then add data-threadtext to any text element -->
<h1 data-threadtext>Your headline</h1>

Framer

Insert → Code → New Component, then paste ThreadText.tsx ↗. It imports the core from esm.sh and exposes every option in the property panel — no build step.

import { /* core */ } from "https://esm.sh/@liiift-studio/threadtext"