uikit icon indicating copy to clipboard operation
uikit copied to clipboard

Generating MSDFs on the fly

Open wrangelvid opened this issue 6 months ago • 7 comments

troika text has quite an interesting approach. They generate the signed distance fields for each glyph on the fly, while also allowing its users to specify characters to preload or even provide already compiled SDFs. The latter would allow a dynamic and simple approach to enable multiple languages. Alternatively, one could generate the whole character set, but save for each glyph and have the current json text format just reference it to enable lazy loading.

wrangelvid avatar May 09 '25 16:05 wrangelvid

AFAIK SDFs are the plan for v2, which will make this easier. In the meantime, we've built a MSDF generator and released it on npm here - https://www.npmjs.com/package/@zappar/msdf-generator (it powers https://pmndrs.github.io/uikit/docs/tutorials/custom-fonts#option-1:-web-based-tool ).

The cost of using this at runtime is ~1mb of wasm, and ~100ms generation time on most mobile devices. One could potentially build some loader/suspense component around it and generate on the fly.

nyan-left avatar Nov 12 '25 15:11 nyan-left

Nice work, I was meaning to do the WASM port at some point! I am surprised you get around 1mb ... are you using Embind and only binding the core or also the extension? Yet to comment on the v2 roadmap, but I think we should be able to get this under 200kb, maybe even lower and lazy load it in UIKit as opposed to going for SDF.

wrangelvid avatar Nov 12 '25 19:11 wrangelvid

Iirc the main chunk of the wasm is Skia PathOps which we use to fix overlaps.

Will see if I can open source the repo next week and get wider thoughts.

nyan-left avatar Nov 13 '25 09:11 nyan-left

Got the wasm down to 300kB https://msdf-staging.zap.works/1.1.0-beta.4/index.html#stress

Mainly through replacing freetype with https://github.com/nothings/stb/blob/master/stb_truetype.h

On most of my devices I'm able to generate a font in 30-60ms.

nyan-left avatar Nov 17 '25 05:11 nyan-left

Wohoo! We mostly use embind with Bazel to really control what parts to port and which dependencies to pull. Glad you found a big dependency to replace! Curious how you did the binding!

Without Bazel, I think one could define an api file with the bare minimum that needs to be bound. That way the compiler only pulls in what it needs (in most cases).

wrangelvid avatar Nov 17 '25 06:11 wrangelvid

Bazel looks cool, It's currently more so the latter :) Shall update the thread once we have something on GitHub!

nyan-left avatar Nov 17 '25 06:11 nyan-left

RFC here https://github.com/pmndrs/uikit/issues/231

nyan-left avatar Nov 19 '25 07:11 nyan-left