Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

Glyph

A typography engine for all your web graphics. Portable font baking, Unicode shaping, paragraph layout, batched text rendering and more.

pnpm add @pmndrs/glyph three
import { glyph, msdf } from '@pmndrs/glyph';
import { ThreeConfig } from '@pmndrs/glyph/three';

await glyph.init();
const three = glyph.handle('main', ThreeConfig);

const Inter = await glyph.fontFace('/fonts/Inter.font.glb', { format: msdf }).load();

const label = three.createText({
  font: Inter,
  text: 'Hello Glyph',
  style: { fontSize: 32, lineHeight: 1.2, color: '#f4f7ff' },
  layout: { align: 'center' },
});

scene.add(label);

glyph.shape();

renderer.render(scene, camera);

Bake fonts

The glyph CLI bakes fonts into glb files containing bitmap, msdf, and/or slug font data. While glyph supports runtime and offline baking, baked fonts require minimal additional processing and load quickly.

pnpm glyph bake --input Inter-Regular.ttf --output Inter.font.glb --bitmap 32 --msdf --slug

Subset a font with --unicodes U+0020-007E to bake only a fixed range or specific glyphs for smaller font assets.
For an icon font, --glyph-map <path> outputs a JSON table keyed by the glyph name in an icon font like Font Awesome or Lucide.

Measure text

You can measure text bounds and font metrics with Text.measure().
Use Text.glyphs() if you need per glyph position and layout metrics, this can incur a small overhead if used before the text is shaped.

const label = three.createText({
  font: Inter,
  text: 'Hello Glyph',
  style: { fontSize: 32, lineHeight: 1.2, color: '#f4f7ff' },
  layout: { align: 'center' },
});

const { width, height, ascent, descent, lineHeight } = label.measure();
const { glyphCount, glyphInkX, glyphInkY } = label.glyphs();
const [x, y] = [glyphInkX[0], glyphInkY[0]];

React Three Fiber

Use @pmndrs/glyph/react for @react-three/fiber integration.

R3F 9.7+ and v10 are supported. For v9, pass an initialized WebGPURenderer through Canvas's async gl factory. See the R3F WebGPU setup guide.

import { GlyphProvider, Text, TextGroup, useSlug } from '@pmndrs/glyph/react';

useSlug.preload('/fonts/Inter.font.glb');

<GlyphProvider handle="hud" fontFaces={{ Inter: '/fonts/Inter.font.glb' }}>
  <Text font="Inter">Hello, HUD</Text>
</GlyphProvider>;

A GlyphProvider can be used to load and map fonts to a string name allowing you to refer to the font by name anywhere under the provider. The provider also acts as an optional Suspense and Error boundary to handle glyph errors or suspend while loading fonts.

Check out the React Three Fiber playground example for more details.

TypeGPU

You can use TypeGPU over TSL in three.js by importing the ThreeConfig from @pmndrs/glyph/three/typegpu.

import { ThreeConfig } from '@pmndrs/glyph/three/typegpu';

For TypeGPU applications, use the defineTypeGpuConfig from @pmndrs/glyph/typegpu.

import { defineTypeGpuConfig } from '@pmndrs/glyph/typegpu';

const root = await tgpu.init();
const CustomTypeGPUConfig = defineTypeGpuConfig({
  root,
  format: navigator.gpu.getPreferredCanvasFormat(),
  //...
});

const handle = glyph.handle('custom:typegpu', CustomTypeGPUConfig);

const encoder = root['~unstable'].createCommandEncoder();
const pass = encoder.beginRenderPass(/* ... */);

glyph.shape();

handle.draw(pass, { width: canvas.clientWidth, height: canvas.clientHeight });

See the TypeGPU hello world example for more details.

TresJS (Vue)

Use @pmndrs/glyph/vue for TresJS integration.

Pass a WebGPURenderer factory to TresCanvas through its renderer prop; Glyph does not support the classic WebGLRenderer.

<script setup lang="ts">
import { GlyphProvider, Text } from '@pmndrs/glyph/vue';
import { preloadSlug } from '@pmndrs/glyph/vue/slug';
import { TresCanvas, type TresRendererSetupContext } from '@tresjs/core';
import { WebGPURenderer } from 'three/webgpu';
import { toValue } from 'vue';

preloadSlug('/fonts/Inter.font.glb');

const createRenderer = (context: TresRendererSetupContext) => new WebGPURenderer({ canvas: toValue(context.canvas) });
</script>

<template>
  <TresCanvas :renderer="createRenderer">
    <GlyphProvider handle="hud" :font-faces="{ Inter: '/fonts/Inter.font.glb' }">
      <Text font="Inter">Hello, HUD</Text>
    </GlyphProvider>
  </TresCanvas>
</template>

A Text renders once its fonts load, and load failures arrive through @error. To load a font inside a component, use useSlug, useMsdf, or useBitmap from @pmndrs/glyph/vue/*. Each returns a ready promise you can await under <Suspense>.

See the TresJS playground example for more details.

Integrate with a custom renderer

The core font engine in glyph is platform and framework neutral. A GlyphConfig defines the implementation of how glyphs are rendered in your target rendering library. ThreeConfig is pre-configured for the three.js integration. A config is made up of mapping, transformations, and config options similar to a bundler config.

export const CustomConfig = defineGlyphConfig({
  // Attach metadata to glyph defined resources
  schema: ExampleSchema,

  // Register supported font formats
  fonts: { default: 'msdf', formats: { bitmap, msdf, slug } },

  // Encode's the raw glyph data into buffer and resource formats your library will need for rendering
  encode: ({ ids }) => ({ descriptor: exampleCodecDescriptor(ids) }),

  // Resolve a glyph resource to a library specific resource (texture, shader, buffer...)
  resolve: ({ format, resourceName, payload }) => {
    return resourceLease(bindResource({ name: resourceName, resource: payload }), () => destroy());
  },

  // Render the glyphs. Decode the plan, synchronize library transforms, dispose of resources when glyph releases them
  renderer: () => {
    const selectedDevice = device ?? new RecordingExampleRendererDevice();
    return {
      decode: (view) => selectedDevice.decode(view),
      syncTransforms: () => undefined,
      dispose: () => selectedDevice.reset(),
    };
  },

  // Create the renderer-specific root extension and connect it to Glyph's retained root
  root: {
    create: (context) => {
      const extension = new CustomRoot(context.fonts, context.services);
      return context.create(extension, {
        boundary: { name: context.name },
        shape: { accepted: (drawList) => extension.accept(drawList) },
      });
    },
  },
});

Just the shaders

The TSL and TypeGPU shaders are exported from @pmndrs/glyph/shaders/tsl and @pmndrs/glyph/shaders/typegpu. You can import them into your custom engine and handle your own font shaping, batching, and loading, while still leveraging the core glyph shaders.

import { bitmapShader, msdfShader, slugShader } from '@pmndrs/glyph/shaders/tsl';
import { bitmapFragment, bitmapVertexSnapped } from '@pmndrs/glyph/shaders/typegpu';

Feature status

Glyph is pre-release and its features may change with time. ✅ Stable is the supported baseline, 🟡 Partial has the gaps listed below, and 🧪 Experimental is available but still being evaluated.

FeatureStatusSupport and limitations
Fonts and rich-text styles✅ StableMixed-font spans, font fallback, size, color, and spacing.
Unicode shaping✅ StableComplex scripts, ligatures, bidirectional text, and grapheme-aware boundaries.
Alignment and justification✅ StableParagraph alignment, word spacing, first-line indent, and paragraph spacing.
Word wrap and box constraints✅ StableUnicode line breaking, width/height constraints, clipping, and ellipsis. Language-specific breaking is future work.
Text measurement✅ StableText bounds, font metrics, and per-glyph layout queries.
Editorial flow and polygon cut-outs✅ StableAuthored regions and exclusions, projected 3D contours, and drop caps.
Icon fonts✅ StableFont-based icons, raster subsetting, and glyph-name maps.
Break-apart glyphs✅ StableDetached glyph and decoration copies with independent transforms. Copies do not follow later source-text edits.
Bitmap rendering✅ StableBaked size-specific strikes. No outline or shadow effects.
MSDF rendering✅ StableMTSDF atlases with outline and hard-shadow effects.
Slug rendering✅ StableVector-outline rendering. No outline or shadow effects.
Three.js✅ StableWebGPU and WebGL2 through WebGPURenderer. Classic WebGLRenderer is not supported.
React Three Fiber✅ StableReact components and font-loading hooks.
TresJS✅ StableVue components and font-loading composables.
TSL shaders✅ StableStandalone Bitmap, MSDF, and Slug shaders that work without the Three.js integration.
Custom renderer integration✅ StableRenderer-neutral GlyphConfig API and custom raster/baker extensions.
Wasm engine and SIMD kernels✅ StableHarfRust shaping and retained Rust layout with SIMD-optimized kernels.
Runtime and offline font baking✅ StableNode API/CLI baking and browser Worker baking for Bitmap, MSDF, and Slug.
Editorial columns🟡 PartialSequential column flow. Automatic column balancing is not implemented.
Text decorations🟡 PartialSolid underline, overline, and strikethrough. Double, dotted, dashed, and wavy styles are not implemented.
CJK🟡 PartialHorizontal shaping and layout. Large-coverage paging and vertical writing are future work.
Direct TypeGPU rendering and shaders🧪 ExperimentalBitmap, MSDF, and Slug in caller-owned WebGPU render passes, plus standalone shader exports.
TypeGPU shaders in Three.js🧪 ExperimentalWebGPU and WebGL2 adapters. Full visual parity with the native TSL path is not yet established.

Roadmap

Future work includes:

  • Color emoji. Color glyph layers and bitmap resources.
  • Micro JS shaping engine. A small alternative for basic shaping.
  • Glyph page cache. On-demand raster pages, residency limits, and eviction for large CJK and icon fonts.
  • Language-aware word breaks. Dictionary segmentation, locale-specific rules, and automatic hyphenation.
  • Expanded editorial layout. Balanced columns and flow around rendered-pixel or depth-buffer occlusion.
  • Vertical writing. Vertical CJK shaping and paragraph layout.
  • Live per-glyph transforms. Deformation that continues to follow retained text updates.

Contribute

Install Git LFS for fixtures and assets. Mise is optional and installs the required tool versions for you. To set up with mise:

git lfs install
git lfs pull
mise trust
mise install
mise exec -- pnpm install
mise exec -- pnpm scripts run repo:hooks:install
mise exec -- pnpm dev

The hook installer writes only the native pre-commit dispatcher in Git's shared common directory. Every worktree in the clone therefore uses the same hook without core.hooksPath or Lefthook configuration. The hook auto-formats and applies safe lint fixes to fully staged source files, re-stages those fixes, and then validates documentation digests. Existing Git LFS hooks such as pre-push, post-checkout, post-commit, and post-merge are not changed.

The benchmark application lives in benches/. pnpm dev opens its interactive harness; pnpm scripts list lists automated benchmarks and fixture generation commands. CI checks out LFS objects before building or testing. Asset paths remain ordinary local files after git lfs pull.

If you already have the pinned Node, pnpm, and Rust versions installed, you can run pnpm commands without mise exec --.

@pmndrs/glyph is ESM-only and MIT licensed.

关于 About

♠️ A typography engine for web graphics.
text-renderingthreejstypegputypographywebgpu

语言 Languages

TypeScript46.1%
Rust36.0%
JavaScript17.5%
CSS0.2%
Vue0.1%
HTML0.1%
Shell0.0%
Dockerfile0.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
331
Total Commits
峰值: 103次/周
Less
More

核心贡献者 Contributors