显示 HN:TTF-DOOM – 一个在 TrueType 字体提示中运行的光线投射器
Show HN: TTF-DOOM – A raycaster running inside TrueType font hinting

原始链接: https://github.com/4RH1T3CT0R7/ttf-doom

## TTF-DOOM:字体中的Doom TTF-DOOM是一个完全可运行的Doom风格光线投射引擎,构建在TrueType字体文件*内部*。该项目利用字体内置的、图灵完备的 hinting 程序(一种用于字形网格拟合的虚拟机),仅使用6.5KB的代码渲染3D图形。 一种定制的领域特定语言(DSL)被编译成TrueType字节码,然后操纵字形“A”——特别是它的16条垂直线——以显示16x16瓦片地图的3D透视图。JavaScript处理玩家输入、敌人逻辑和射击,通过字体变化设置将坐标传递给字体。字体执行光线投射和墙壁渲染,而JavaScript则叠加HUD和敌人。 该项目克服了TrueType算术中的一些缺陷和有限的调用栈带来的显著障碍。它利用巧妙的解决方法来模拟循环,规避损坏的乘法/除法和递归函数调用。调试模式(通过按下Tab键激活)可以实时可视化字体变化轴。 与llama.ttf等使用WebAssembly的类似项目不同,TTF-DOOM利用的是1991年原始的TrueType hinting字节码。

## TTF-DOOM:字体内的DOOM 一位开发者 (4RH1T3CT0R) 创建了一个功能性的光线投射引擎——类似于*DOOM*——完全*位于*TrueType字体的 hinting 字节码中。该引擎利用字体的图灵完备虚拟机,使用字形轮廓和字体变体轴渲染出粗糙的、*Wolfenstein*风格的视角。 这个6.5KB的程序使用了13个函数和795个存储槽,巧妙地规避了诸如有缺陷的乘法指令和缺乏`while`循环(通过递归实现)等问题。JavaScript处理游戏逻辑,将玩家坐标传递给字体进行渲染——本质上将字体变成了一个奇特的GPU。 尽管存在限制(例如递归深度限制为64),该引擎在Chrome浏览器中仍能达到30-60fps,主要瓶颈在于浏览器 hinting 的重新评估。 现场演示可在 [https://4rh1t3ct0r7.github.io/ttf-doom/](https://4rh1t3ct0r7.github.io/ttf-doom/) 找到。
相关文章

原文

A DOOM-style raycaster that runs inside a TrueType font's hinting program.

Play in Browser

TrueType fonts have a built-in virtual machine for grid-fitting glyphs. It's got a stack, storage slots, arithmetic, conditionals, function calls - and it turns out it's Turing-complete. I wanted to see if I could get it to render 3D graphics.

The font file contains a DDA raycasting engine written in TrueType bytecode. The glyph "A" has 16 vertical bar contours, and the hinting program raycasts against a 16x16 tile map and repositions those bars using SCFS instructions to form a 3D perspective view. The whole thing is 6.5 KB.

JS handles movement, enemies, and shooting, then passes coordinates to the font through font-variation-settings. The font does the raycasting and wall rendering. Canvas overlay draws enemies, weapon, and HUD on top.

I wrote a small compiler that takes a custom DSL and outputs TrueType hinting assembly. The DSL looks like a stripped-down C:

func raycast(col: int) -> int:
    var ra: int = player_angle + col * 3 - FOV_HALF
    var dx: int = get_cos(ra)
    var dy: int = get_sin(ra)
    ...

This compiles to TT bytecode - FDEF, CALL, RS, WS, SCFS, etc. - and gets injected into a .ttf file along with sin/cos lookup tables and the map data. The compiler handles variable allocation (everything goes into TT storage slots), function definitions (FDEF/ENDF), and a few other things like fixed-point math.

The pipeline: doom.doom -> lexer -> parser -> codegen -> doom.ttf

TrueType arithmetic is insane

MUL does (a*b)/64. Not a*b. Everything is F26Dot6 fixed-point internally. So 1 * 4 = 0. I lost probably two days to this before I found a workaround: DIV(a, 1) returns a * 64 (DIV is equally broken), then MUL(a*64, b) = (a*64*b)/64 = a*b.

There's no WHILE instruction either. Loops compile to recursive FDEFs, and FreeType caps the call stack at ~64 frames. 16 columns x 14 ray steps barely fits.

return inside a recursive-while doesn't exit. It pushes a value and keeps going. Everything had to be rewritten with hit flags.

SCFS takes F26Dot6 pixel coordinates, not font units - took me a while to figure out why every bar was a tiny speck. Chrome caches hinted glyphs and sometimes skips re-hinting when axes change - fixed with per-frame jitter. SVTCA[0] selects Y, [1] selects X.

The font just renders walls. JS does everything else.

Player position and angle go into three font variation axes (MOVX, MOVY, TURN). JS stuffs coordinates into font-variation-settings every frame. Browser re-hints the glyph. Shape changes.

Press Tab in the demo to see what's going on under the hood:

git clone https://github.com/4RH1T3CT0R7/ttf-doom.git
cd ttf-doom
pip install fonttools freetype-py pygame pytest
python game/build.py
python -m http.server 8765

Open http://localhost:8765/hosts/browser/index.html in Chrome or Edge. WASD to move, arrows to turn, Space to shoot. Press Tab for a debug overlay showing the font variation axes in real-time.

compiler/       DSL -> TrueType assembly (lexer, parser, codegen)
fontgen/        font builder, glyph generator, sin/cos tables
game/           raycaster source (doom.doom) and build script
hosts/browser/  browser demo
hosts/python/   pygame host (for development)
tests/          451 tests
doom.ttf        the playable font (6,580 bytes)

llama.ttf also runs computation in a font, but it uses HarfBuzz's WASM shaper - basically a WebAssembly runtime bolted onto font shaping. TTF-DOOM uses the actual TrueType hinting bytecode that Apple shipped in 1991 for grid-fitting glyphs. Different VM entirely.

Apache 2.0

联系我们 contact @ memedata.com