npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@liiift-studio/threadtext

v0.4.4

Published

Render any text as photorealistic procedural satin-stitch embroidery, in real time in the browser, from the font's glyph geometry

Readme

threadText

Render any text as photorealistic, procedural satin-stitch embroidery — in real time, in the browser, from the font's actual glyph geometry.

npm license types dependencies gzip

Homepage: threadtext.com (live, interactive demo) · npm: @liiift-studio/threadtext

Framework-agnostic core (createThreadText), a React hook + component (useThreadText / <ThreadText>), and Framer / Webflow ports. Zero required dependencies; react/react-dom are optional peers.

See it move. It sews itself in stitch-by-stitch and reacts to the cursor, so a still can't fully show it — the live demo at threadtext.com is the fastest way to judge it.


What it is

Give it a word set in any loaded (variable) font and it renders it 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. It's photorealistic by construction (real thread geometry + lighting), not a raster filter and not an AI image.

It's built for single words / short display type — a hero word, a logotype, a stitched headline — not body copy.

How it works

Each step below is what actually produces the thread look — the plain-language idea first, the precise technique in parentheses:

  1. Draw the glyphs to an offscreen canvas from the loaded font (rasterise; variable-font axes applied via canvas fontVariationSettings).
  2. Measure how deep inside each stroke every pixel is (an exact Euclidean signed-distance field).
  3. Work out which way the thread should run — always across the stroke, fanning smoothly around curves (a flow field, smoothed in double-angle orientation space so the two opposite edges of a stroke agree instead of cancelling).
  4. Lay the stitches by stamping many small, pre-lit thread images along that flow (a reused set of pre-shaded thread sprites — ~20 brightness variants placed as thousands of individual stitches).
  5. Lift it into 3D so each thread catches light and casts a soft crease (a dome-shade / normal map over the transparent ground).
  6. Sew it in one cross-row at a time as an animation, and add a subtle cursor-driven sheen as you move over it.

The heavy geometry pass (steps 2–5) runs in a Web Worker so typing and live edits stay smooth; where a Worker isn't available it falls back to running synchronously on the main thread (see Requirements).

Use it

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

// Declare the face (any @font-face / next/font / CSS Font Loading API) — the core
// waits for it to load before drawing, so you don't have to await it yourself.
const host = document.getElementById('host')
if (host) {
  const thread = createThreadText(host, {
    text: 'Thread',
    font: '"Your Font", Georgia, serif',   // glyph geometry drives the stitch flow
    weight: 680,
  })

  thread.setText('Threads')                            // re-fit to width and redraw instantly
  thread.update({ threadColor: '#e6c200', fill: 0.8 }) // live changes — instant, no re-sew
  thread.replay()                                      // re-run the sew-in animation
  thread.resize()                                      // re-fit to the container
  thread.destroy()                                     // cancel rAF, remove listeners, free canvases
}

The host element needs a resolved width — the word is fitted to host.getBoundingClientRect().width. createThreadText is SSR-safe: called without a DOM it returns an inert no-op handle, so it won't crash a server render.

React

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

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

Next.js / App Router: the React entry uses useRef/useLayoutEffect, so it's a Client Component — put 'use client' at the top of the file that imports <ThreadText> (or your own wrapper). The framework-agnostic core is SSR-safe on its own; only the React bindings need the client boundary.

For a custom container or your own imperative control, use the hook — it creates the instance, applies option changes live, and tears everything down (ResizeObserver + destroy()) on unmount:

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

function Stitched() {
  const ref = useThreadText<HTMLDivElement>({ text: 'Thread', weight: 680, threadColor: '#e6c200' })
  return <div ref={ref} style={{ width: '100%' }} />
}

Framer & Webflow

Framer — add src/framer/ThreadText.tsx as a code component (Insert → Code → New Component, or host it and add by URL). It imports the core straight from esm.sh, so there's no build step, and every option is exposed as a Framer property control.

Webflow — drop the self-contained embed bundle (no React, no module loader) into an Embed element and mark any element with data-threadtext:

<script src="https://unpkg.com/@liiift-studio/threadtext/dist/threadtext.webflow.min.js"></script>

<div data-threadtext
     data-tt-text="Thread"
     data-tt-font='"Your Font", serif'
     data-tt-weight="680"
     data-tt-thread-color="#e6c200"
     data-tt-stitch-mode="satin"
     data-tt-sew-style="machine"></div>

The bundle auto-initialises every [data-threadtext] element and exposes a small window.ThreadText API for manual control. Supported attributes: data-tt-text, data-tt-font, data-tt-weight, data-tt-thread-color, data-tt-thread-color2, data-tt-color-mode, data-tt-backstitch, data-tt-outline-color, data-tt-fill, data-tt-align, data-tt-pitch, data-tt-stitch-mode, data-tt-sew-style, data-tt-sew-rate, data-tt-sheen, data-tt-animate, data-tt-editable, data-tt-axes (JSON).


Options

All fields on ThreadTextOptions. Most can be changed live with instance.update(...) — colour, font, weight, size (fill), pitch, stitch mode, sew style/rate, sheen, axes and editability all redraw instantly without re-running the sew-in. Two exceptions: text changes go through instance.setText(...), and reducedMotion is fixed at construction (changing it recreates the instance).

  • text — the word to embroider.
  • font — CSS font-family of an already-loaded font; its glyph geometry drives the stitch flow.
  • weight — numeric font weight (drives the wght axis via the standard font shorthand).
  • axes — variable-font axes, e.g. { opsz: 40, SOFT: 60 }, applied via canvas fontVariationSettings (Chrome/Edge/Safari); ignored where unsupported.
  • threadColor — floss colour.
  • colorMode'solid' (default) · 'twotone' (two colours as alternating threads packed side by side) · 'gradient' (a smooth colour transition across the word). 'twotone'/'gradient' use threadColor2 as the second colour.
  • backstitch — add a darker running-stitch outline traced around each glyph (sews in last); outlineColor sets its colour (defaults to a darkened threadColor).
  • fill — size: the fraction of the container width the word spans (it re-fits on load/resize).
  • align — horizontal placement of the word in the canvas when fill leaves spare width: 'center' (default) · 'left' · 'right'.
  • pitch — thread spacing (how tightly the stitches are packed across each stroke).
  • stitchMode'satin' · 'cross' · 'chain' · 'running' textures (below).
  • sewStyle'machine' (satin rows in parallel) or 'hand' (one letter at a time, widest threads near the top first, thin edges last).
  • sewRate · animate — sew-in speed and whether it plays.
  • reducedMotion — force-skip the sew-in animation and draw the finished piece in one frame (also honoured automatically when the OS prefers-reduced-motion is set).
  • sheen — cursor-following highlight.
  • editable + onTextChange — make the surface typeable (backed by a real <input>, so touch keyboards and IME work). Call instance.focus() to focus it programmatically.

Requirements & browser support

  • Canvas 2D — required. The whole render is drawn to stacked <canvas> elements.
  • Web Worker + Blob + URL.createObjectURL — used to run the geometry pass off the main thread. Under a strict Content-Security-Policy the Worker is built from a blob: URL, so you need worker-src blob: (or script-src blob:). If Worker creation is blocked or unavailable, threadText degrades gracefully and runs the same pass synchronously on the main thread — correct output, but heavy edits can jank on very large words.
  • fontVariationSettings on canvas (Chrome/Edge/Safari) — needed only for the axes option; feature-detected and skipped where unsupported.
  • prefers-reduced-motion — honoured automatically (skips the sew-in animation).
  • Size — ~10 kB gzipped (ESM), ~9 kB for the standalone Webflow bundle. Zero runtime dependencies (react/react-dom are optional peers), tree-shakeable (sideEffects: false).
  • Stability — pre-1.0 (0.x); the option and instance API above is what's shipped, but pin the version if you depend on exact visual output.

Development

This repo is a git submodule of the type-tools monorepo. To work on the package itself:

git clone https://github.com/Liiift-Studio/ThreadText.git
cd ThreadText
npm install
npm test            # vitest (happy-dom)
npm run build       # ESM + CJS + .d.ts  (vite)
npm run build:webflow  # standalone window.ThreadText IIFE bundle
npm run lint        # tsc --noEmit

Source layout:

| Path | What it is | |---|---| | src/index.ts | Public exports (createThreadText, useThreadText, ThreadText, THREAD_TEXT_CLASSES) | | src/core/threadText.ts | Framework-agnostic renderer (createThreadText) — no React imports | | src/core/types.ts | ThreadTextOptions, ThreadTextInstance, THREAD_TEXT_CLASSES | | src/react/ | useThreadText hook + <ThreadText> component | | src/framer/ThreadText.tsx | Framer code component (imports core from esm.sh) | | src/webflow/embed.ts | Webflow auto-init embed → dist/threadtext.webflow.min.js | | src/__tests__/ | vitest suites (core, worker assembly, webflow embed) |

The landing site + interactive demo live in site/ (Next.js). Issues and PRs: github.com/Liiift-Studio/ThreadText/issues.

Contributing note: this repo is a submodule of the type-tools monorepo, and the files under site/ config, vercel.json, and .gitignore are auto-synced from the parent (type-tools/shared/) — edits to those here are overwritten on the next sync. The package source (src/) and the per-tool site files (site/src/app/page.tsx, Demo.tsx, layout.tsx, globals.css) are safe to edit directly.


The type-tools family

threadText is one of type-tools — a suite of small, focused typographic libraries by Liiift Studio, each doing one thing that's hard or impossible in CSS alone. A few siblings:

  • ragtooth — deliberate sawtooth ragged-edge line breaking.
  • floodText — per-character variable-font wave animation.
  • magnetType — cursor-field per-character axis variation.
  • opszStepper — optical-size font hot-swap by font-size.
  • hoverBoldly — bold-on-hover with no layout shift.

See the full family for all of them.


Part of type-tools by Liiift Studio. MIT.