slug-font-rendering
Reference HLSL shader implementations for the Slug font rendering algorithm, enabling high-quality GPU-accelerated vector font and glyph rendering.
Best use case
slug-font-rendering is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Reference HLSL shader implementations for the Slug font rendering algorithm, enabling high-quality GPU-accelerated vector font and glyph rendering.
Teams using slug-font-rendering should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/slug-font-rendering/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How slug-font-rendering Compares
| Feature / Agent | slug-font-rendering | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Reference HLSL shader implementations for the Slug font rendering algorithm, enabling high-quality GPU-accelerated vector font and glyph rendering.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Slug Font Rendering Algorithm
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
Slug is a reference implementation of the Slug font rendering algorithm — a GPU-accelerated technique for rendering vector fonts and glyphs at arbitrary scales with high quality anti-aliasing. It works by encoding glyph outlines as lists of quadratic Bézier curves and line segments, then resolving coverage directly in fragment shaders without pre-rasterized textures.
**Paper:** [JCGT 2017 — Slug Algorithm](https://jcgt.org/published/0006/02/02/)
**Blog (updates):** [A Decade of Slug](https://terathon.com/blog/decade-slug.html)
**License:** MIT — Patent dedicated to public domain. Credit required if distributed.
---
## What Slug Does
- Renders TrueType/OpenType glyphs entirely on the GPU
- No texture atlases or pre-rasterization needed
- Scales to any resolution without quality loss
- Anti-aliased coverage computed per-fragment using Bézier math
- Works with any rendering API that supports programmable shaders (D3D11/12, Vulkan, Metal via translation)
---
## Repository Structure
```
Slug/
├── slug.hlsl # Core fragment shader — coverage computation
├── band.hlsl # Band-based optimization for glyph rendering
├── curve.hlsl # Quadratic Bézier and line segment evaluation
├── README.md
```
---
## Installation / Integration
Slug is a **reference implementation** — you integrate the HLSL shaders into your own rendering pipeline.
### Step 1: Clone the Repository
```bash
git clone https://github.com/EricLengyel/Slug.git
```
### Step 2: Include the Shaders
Copy the `.hlsl` files into your shader directory and include them in your pipeline:
```hlsl
#include "slug.hlsl"
#include "curve.hlsl"
```
### Step 3: Prepare Glyph Data on the CPU
You must preprocess font outlines (TrueType/OTF) into Slug's curve buffer format:
- Decompose glyph contours into quadratic Bézier segments and line segments
- Upload curve data to a GPU buffer (structured buffer or texture buffer)
- Precompute per-glyph "band" metadata for the band optimization
---
## Core Concepts
### Glyph Coordinate System
- Glyph outlines live in **font units** (typically 0–2048 or 0–1000 per em)
- The fragment shader receives a position in glyph space via interpolated vertex attributes
- Coverage is computed by counting signed curve crossings in the Y direction (winding number)
### Curve Data Format
Each curve entry in the GPU buffer stores:
```hlsl
// Line segment: p0, p1
// Quadratic Bézier: p0, p1 (control), p2
struct CurveRecord
{
float2 p0; // Start point
float2 p1; // Control point (or end point for lines)
float2 p2; // End point (unused for lines — flagged via type)
// Type/flags encoded separately or in padding
};
```
### Band Optimization
The glyph bounding box is divided into horizontal **bands**. Each band stores only the curves that intersect it, reducing per-fragment work from O(all curves) to O(local curves).
---
## Key Shader Code & Patterns
### Fragment Shader Entry Point (Conceptual Integration)
```hlsl
// Inputs from vertex shader
struct PS_Input
{
float4 position : SV_Position;
float2 glyphCoord : TEXCOORD0; // Position in glyph/font units
// Band index or precomputed band data
nointerpolation uint bandOffset : TEXCOORD1;
nointerpolation uint curveCount : TEXCOORD2;
};
// Glyph curve data buffer
StructuredBuffer<float4> CurveBuffer : register(t0);
float4 PS_Slug(PS_Input input) : SV_Target
{
float coverage = ComputeGlyphCoverage(
input.glyphCoord,
CurveBuffer,
input.bandOffset,
input.curveCount
);
// Premultiplied alpha output
float4 color = float4(textColor.rgb * coverage, coverage);
return color;
}
```
### Quadratic Bézier Coverage Computation
The heart of the algorithm — computing signed coverage from a quadratic Bézier:
```hlsl
// Evaluate whether a quadratic bezier contributes to coverage at point p
// p0: start, p1: control, p2: end
// Returns signed coverage contribution
float QuadraticBezierCoverage(float2 p, float2 p0, float2 p1, float2 p2)
{
// Transform to canonical space
float2 a = p1 - p0;
float2 b = p0 - 2.0 * p1 + p2;
// Find t values where bezier Y == p.y
float2 delta = p - p0;
float A = b.y;
float B = a.y;
float C = p0.y - p.y;
float coverage = 0.0;
if (abs(A) > 1e-6)
{
float disc = B * B - A * C;
if (disc >= 0.0)
{
float sqrtDisc = sqrt(disc);
float t0 = (-B - sqrtDisc) / A;
float t1 = (-B + sqrtDisc) / A;
// For each valid t in [0,1], compute x and check winding
if (t0 >= 0.0 && t0 <= 1.0)
{
float x = (A * t0 + 2.0 * B) * t0 + p0.x + delta.x;
// ... accumulate signed coverage
}
if (t1 >= 0.0 && t1 <= 1.0)
{
float x = (A * t1 + 2.0 * B) * t1 + p0.x + delta.x;
// ... accumulate signed coverage
}
}
}
else
{
// Degenerate to linear case
float t = -C / (2.0 * B);
if (t >= 0.0 && t <= 1.0)
{
float x = 2.0 * a.x * t + p0.x;
// ... accumulate signed coverage
}
}
return coverage;
}
```
### Line Segment Coverage
```hlsl
// Signed coverage contribution of a line segment from p0 to p1
float LineCoverage(float2 p, float2 p0, float2 p1)
{
// Check Y range
float minY = min(p0.y, p1.y);
float maxY = max(p0.y, p1.y);
if (p.y < minY || p.y >= maxY)
return 0.0;
// Interpolate X at p.y
float t = (p.y - p0.y) / (p1.y - p0.y);
float x = lerp(p0.x, p1.x, t);
// Winding: +1 if p is to the left (inside), -1 if right
float dir = (p1.y > p0.y) ? 1.0 : -1.0;
return (p.x <= x) ? dir : 0.0;
}
```
### Anti-Aliasing with Partial Coverage
For smooth edges, use the distance to the nearest curve for sub-pixel anti-aliasing:
```hlsl
// Compute AA coverage using partial pixel coverage
// windingNumber: integer winding from coverage pass
// distToEdge: signed distance to nearest curve (in pixels)
float AntiAliasedCoverage(int windingNumber, float distToEdge)
{
// Non-zero winding rule
bool inside = (windingNumber != 0);
// Smooth transition at edges using clamp
float edgeCoverage = clamp(distToEdge + 0.5, 0.0, 1.0);
return inside ? edgeCoverage : (1.0 - edgeCoverage);
}
```
---
## Vertex Shader Pattern
```hlsl
struct VS_Input
{
float2 position : POSITION; // Glyph quad corner in screen/world space
float2 glyphCoord : TEXCOORD0; // Corresponding glyph-space coordinate
uint bandOffset : TEXCOORD1; // Offset into curve buffer for this glyph
uint curveCount : TEXCOORD2; // Number of curves in band
};
struct VS_Output
{
float4 position : SV_Position;
float2 glyphCoord : TEXCOORD0;
nointerpolation uint bandOffset : TEXCOORD1;
nointerpolation uint curveCount : TEXCOORD2;
};
VS_Output VS_Slug(VS_Input input)
{
VS_Output output;
output.position = mul(float4(input.position, 0.0, 1.0), WorldViewProjection);
output.glyphCoord = input.glyphCoord;
output.bandOffset = input.bandOffset;
output.curveCount = input.curveCount;
return output;
}
```
---
## CPU-Side Data Preparation (Pseudocode)
```cpp
// 1. Load font file and extract glyph outlines
FontOutline outline = LoadGlyphOutline(font, glyphIndex);
// 2. Decompose to quadratic Beziers (TrueType is already quadratic)
// OTF cubic curves must be approximated/split into quadratics
std::vector<SlugCurve> curves = DecomposeToQuadratics(outline);
// 3. Compute bands
float bandHeight = outline.bounds.height / NUM_BANDS;
std::vector<BandData> bands = ComputeBands(curves, NUM_BANDS, bandHeight);
// 4. Upload to GPU
UploadStructuredBuffer(curveBuffer, curves.data(), curves.size());
UploadStructuredBuffer(bandBuffer, bands.data(), bands.size());
// 5. Per glyph instance: store bandOffset and curveCount per band
// in vertex data so the fragment shader can index directly
```
---
## Render State Requirements
```hlsl
// Blend state: premultiplied alpha
BlendState SlugBlend
{
BlendEnable = TRUE;
SrcBlend = ONE; // Premultiplied
DestBlend = INV_SRC_ALPHA;
BlendOp = ADD;
SrcBlendAlpha = ONE;
DestBlendAlpha = INV_SRC_ALPHA;
BlendOpAlpha = ADD;
};
// Depth: typically write disabled for text overlay
DepthStencilState SlugDepth
{
DepthEnable = FALSE;
DepthWriteMask = ZERO;
};
// Rasterizer: no backface culling (glyph quads are 2D)
RasterizerState SlugRaster
{
CullMode = NONE;
FillMode = SOLID;
};
```
---
## Common Patterns
### Rendering a String
```cpp
// For each glyph in string:
for (auto& glyph : string.glyphs)
{
// Emit a quad (2 triangles) covering the glyph bounding box
// Each vertex carries:
// - screen position
// - glyph-space coordinate (the same corner in font units)
// - bandOffset + curveCount for the fragment shader
float2 min = glyph.screenMin;
float2 max = glyph.screenMax;
float2 glyphMin = glyph.fontMin;
float2 glyphMax = glyph.fontMax;
EmitQuad(min, max, glyphMin, glyphMax,
glyph.bandOffset, glyph.curveCount);
}
```
### Scaling Text
Scaling is handled entirely on the CPU side by transforming the screen-space quad. The glyph-space coordinates stay constant — the fragment shader always works in font units.
```cpp
float scale = desiredPixelSize / font.unitsPerEm;
float2 screenMin = origin + glyph.fontMin * scale;
float2 screenMax = origin + glyph.fontMax * scale;
```
---
## Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Glyph appears hollow/inverted | Winding order reversed | Check contour orientation; TrueType uses clockwise for outer contours |
| Jagged edges | Anti-aliasing not applied | Ensure distance-to-edge is computed and used in final coverage |
| Performance poor | Band optimization not active | Verify per-fragment curve count is small (< ~20); increase band count |
| Cubic curves not rendering | OTF cubic Béziers unsupported natively | Split cubics into quadratic approximations on CPU |
| Artifacts at glyph overlap | Curves not clipped to band | Clip curve Y range to band extents before upload |
| Black box instead of glyph | Blend state wrong | Use premultiplied alpha blend (ONE, INV_SRC_ALPHA) |
| Missing glyphs | Band offset incorrect | Validate bandOffset indexing aligns with buffer layout |
---
## Credits & Attribution
Per the license: if you distribute software using this code, **you must give credit** to Eric Lengyel and the Slug algorithm.
Suggested attribution:
> Font rendering uses the Slug Algorithm by Eric Lengyel (https://jcgt.org/published/0006/02/02/)
---
## References
- [Slug Algorithm Paper (JCGT 2017)](https://jcgt.org/published/0006/02/02/)
- [A Decade of Slug — Blog Post](https://terathon.com/blog/decade-slug.html)
- [GitHub Repository](https://github.com/EricLengyel/Slug)Related Skills
```markdown
---
zeroboot-vm-sandbox
Sub-millisecond VM sandboxes for AI agents using copy-on-write KVM forking via Zeroboot
yourvpndead-vpn-detection
Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root
xata-postgres-platform
Expert skill for Xata open-source cloud-native Postgres platform with copy-on-write branching, scale-to-zero, and Kubernetes deployment
x-mentor-skill-nuwa
AI-powered X (Twitter) content strategy skill that distills methodologies from 6 top creators + open-source algorithm data into actionable writing, growth, and monetization guidance.
wx-favorites-report
End-to-end pipeline to extract, decrypt, and visualize WeChat Mac favorites from encrypted SQLite DB into an interactive HTML report.
wterm-web-terminal
Web terminal emulator with Zig/WASM core, DOM rendering, and React/vanilla JS bindings
worldmonitor-intelligence-dashboard
Real-time global intelligence dashboard with AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking
witr-process-inspector
CLI and TUI tool that explains why processes, services, and ports are running by tracing causality chains across supervisors, containers, and shells.
wildworld-dataset
WildWorld large-scale action-conditioned world modeling dataset with 108M+ frames from a photorealistic ARPG game, featuring per-frame annotations, 450+ actions, and explicit state information for generative world modeling research.
whatcable-macos-usb-inspector
macOS menu bar app that identifies USB-C cable capabilities and charging diagnostics using IOKit
wewrite-wechat-ai-publishing
Full-pipeline AI skill for WeChat Official Account articles — hotspot fetching, topic selection, writing, SEO, image generation, formatting, and draft box publishing.