Skip to content

1sh22/pretex

Repository files navigation

pretex

A typographic ASCII art playground built on top of Pretext — a fast, accurate, and comprehensive text measurement and layout library by Cheng Lou. pretex explores what becomes possible when you have precise, DOM-independent text metrics: generative ASCII rendering with proportional fonts, live camera-to-ASCII conversion, and editorially composed text that flows dynamically around draggable objects on a canvas.


Overview

pretex operates in three distinct modes, toggled from the sidebar:

Generative — A canvas samples brightness from a procedural shape (circle, wave, grid, spiral, radial, noise) and maps it to characters from a chosen set. The renderer supports both monospace and proportional fonts (Georgia in bold, normal, and italic weight bands), meaning glyphs vary not just in shade but in visual weight and width. Six cursor interaction modes — shape warp, void, spotlight, ink, lens, and repel — let you distort or reveal the field in real time.

Camera — Captures a live webcam feed, downsamples it to a grid, and renders the result as a scrolling ASCII <pre>. Optional colorization modes map luminance or chroma values directly to the text. Frame rate is governed by an RAF-based animation loop with adjustable speed and pause controls.

Editorial — Uses @chenglou/pretext to lay out long-form article text line by line across a canvas. Draggable glowing orbs act as obstacles: text reflows around them on every frame, wrapping around their bounding boxes. This mode demonstrates Pretext's variable-width layout API (layoutNextLine) working inside a live animation loop — something impossible to do with CSS alone.


Built with


Getting started

Prerequisites

  • Node.js 20 or later
  • npm

Installation

git clone <repo-url>
cd pretext-visual
npm install

Development

npm run dev

Open http://localhost:3000 in your browser.

Production build

npm run build
npm start

Project structure

pretext-visual/
├── app/
│   ├── layout.tsx          # Root layout, metadata, global styles
│   ├── page.tsx            # Client root: AppState, mode routing, ResizeObserver
│   └── globals.css         # Tailwind v4 import, base resets, scrollbar styles
├── components/
│   ├── AsciiCanvas.tsx     # Generative canvas renderer
│   ├── CameraAscii.tsx     # Webcam capture and ASCII rendering
│   ├── EditorialCanvas.tsx # Pretext-powered text-around-orbs canvas
│   ├── Sidebar.tsx         # All control panels per mode
│   └── Topbar.tsx          # Navigation bar
├── hooks/
│   ├── useAnimationLoop.ts # RAF loop with speed and pause support
│   └── useCamera.ts        # getUserMedia, frame capture to ImageData
├── lib/
│   ├── brightness.ts       # Perlin-style noise, per-shape brightness sampling
│   ├── charPicker.ts       # Map brightness to glyph by density or width
│   ├── glyphMeasure.ts     # Build glyph tables via canvas measureText
│   └── imageProcessing.ts  # Luma, contrast, gamma, sharpness, heatmap color
├── types/
│   └── index.ts            # AppState and all shared types
├── next.config.ts
├── tsconfig.json
└── package.json

How Pretext is used

The editorial mode is the primary demonstration of Pretext's layout engine. Rather than measuring text height with getBoundingClientRect or estimating line counts, the canvas calls prepareWithSegments once on the article text, then drives layoutNextLine inside the animation loop:

import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'

const prepared = prepareWithSegments(article, font)
let cursor: LayoutCursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = topPadding

while (true) {
  const orb = orbAtRow(y)
  const maxWidth = orb ? canvasWidth - orb.radius * 2 : canvasWidth
  const line = layoutNextLine(prepared, cursor, maxWidth)
  if (line === null) break

  ctx.fillText(line.text, orb ? orb.radius * 2 : 0, y)
  cursor = line.end
  y += lineHeight
}

Because layoutNextLine accepts a different maxWidth per line and performs no DOM access, this runs entirely on the main thread inside a requestAnimationFrame callback without any layout reflow. As orbs move, the text reflows on every frame.

For more on the Pretext API — including prepare, layout, walkLineRanges, measureLineStats, and the rich-inline helper — see the Pretext repository.


Configuration note

Next.js 16 detects a lockfile at the monorepo root (/Users/ishaanchoubey/package-lock.json) and emits a workspace-root warning. To silence it, set turbopack.root in next.config.ts:

const nextConfig: NextConfig = {
  turbopack: {
    root: __dirname,
  },
}

See the Turbopack root directory docs for details.


License

MIT. Copyright 2026 Ishaan Choubey.

About

Typographic ASCII renderer with generative, camera, and editorial modes. Built on @chenglou/pretext for accurate, reflow-free text layout on canvas.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors