File size: 5,064 Bytes
a9df2c1
b3f0c1a
 
 
a9df2c1
b3f0c1a
a9df2c1
 
b3f0c1a
 
a9df2c1
fc2a05d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
---
title: FontMap
short_description: Visual font explorer powered by FontCLIP
emoji: πŸ—ΊοΈ
colorFrom: indigo
colorTo: blue
sdk: static
pinned: false
app_build_command: "CI=false npm run build"
app_file: "build/index.html"
---

# FontMap

An interactive map of 1,100+ Google Fonts organized by visual similarity, using [FontCLIP](https://github.com/kinit-sk/FontCLIP) embeddings and UMAP dimensionality reduction.

Inspired by [IDEO's Font Map](https://medium.com/ideo-stories/organizing-the-world-of-fonts-with-ai-7d9e49ff2b25) (2017, Kevin Ho) β€” rebuilt from scratch with modern ML, fully open source, and documented.

**[Live demo](https://huggingface.co/spaces/tfrere/fontmap)**

![FontMap screenshot](https://huggingface.co/spaces/tfrere/fontmap/resolve/main/public/screenshot.png)

## Features

- **Visual similarity map** β€” fonts that look alike are physically close together
- **1,192 Google Fonts** β€” full catalog, family variants merged
- **Click any font** β€” see details + 5 nearest visual neighbors
- **Filter by category** β€” serif, sans-serif, display, handwriting, monospace
- **Category colors** β€” toggle color coding to see how clusters map to official categories
- **Search** β€” find any font by name
- **Zoom & pan** β€” explore the full map with smooth D3.js navigation

## How it works

```
Font images ──→ FontCLIP (512D) ──→ PCA (50D) ──→ UMAP (2D) ──→ Interactive map
                                         ↓
                                    k-NN (similar fonts)
```

1. **Render** β€” each font is rendered as a 224Γ—224 composite glyph image
2. **Embed** β€” [FontCLIP](https://github.com/kinit-sk/FontCLIP) (CLIP ViT-B/32 fine-tuned for typography) encodes each image into a 512-dimensional vector
3. **Reduce** β€” PCA compresses to 50D, then UMAP with spectral initialization projects to 2D (n_neighbors=12, min_dist=1.0)
4. **Merge** β€” font family variants (Regular, Bold, Italic…) are fused into a single representative point
5. **Neighbors** β€” k-NN is computed in the original 512D space for "similar fonts" recommendations
6. **Visualize** β€” React + D3.js renders all glyphs from a single SVG sprite (zero individual network requests)

## Getting started

### Run locally

```bash
git clone https://github.com/tfrere/fontmap.git
cd fontmap
npm install
npm start
```

Open [http://localhost:3000](http://localhost:3000).

### Build for production

```bash
npm run build
```

### Deploy to Hugging Face Spaces

The project is configured for [HF Spaces static SDK](https://huggingface.co/docs/hub/spaces-sdks-static) with a build step:

```yaml
sdk: static
app_build_command: "CI=false npm run build"
app_file: "build/index.html"
```

## Regenerating the map

The embedding pipeline lives in `src/typography/new-pipe/`. See its [README](src/typography/new-pipe/README.md) for full instructions.

**Quick summary:**

```bash
# 1. Generate FontCLIP embeddings (requires Python + GPU, ~1h one-time)
cd src/typography/new-pipe/python-pipeline
python run_fontclip.py

# 2. Test different UMAP configurations
cd ..
npm run batch-umap
npm run copy-to-debug
# Compare visually at http://localhost:3000/#/debug-umap

# 3. Deploy chosen config to production
npm run deploy fontclip-spectral
```

## Project structure

```
fontmap/
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ data/
β”‚   β”‚   β”œβ”€β”€ char/              # Individual glyph SVGs (~1,200 files)
β”‚   β”‚   β”œβ”€β”€ sentences/         # Sentence preview SVGs
β”‚   β”‚   β”œβ”€β”€ font-sprite.svg    # All glyphs in a single sprite (3 MB)
β”‚   β”‚   └── typography_data.json  # Font positions + metadata
β”‚   └── debug-umap/            # Pre-computed UMAP configs for comparison
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ FontMap/           # Main map component (production)
β”‚   β”‚   β”œβ”€β”€ DebugUMAP/         # UMAP comparison tool (development)
β”‚   β”‚   └── FontMapV2/         # Experimental canvas renderer
β”‚   β”œβ”€β”€ hooks/                 # Shared hooks (data loading, dark mode)
β”‚   β”œβ”€β”€ store/                 # Zustand state management
β”‚   └── typography/new-pipe/   # Embedding + UMAP generation pipeline
β”‚       └── python-pipeline/   # FontCLIP + PCA + UMAP (Python)
└── package.json
```

## Tech stack

| Layer | Tech |
|-------|------|
| Frontend | React 19, D3.js v7, Zustand |
| Rendering | SVG sprite + D3 zoom/pan |
| Embeddings | FontCLIP (CLIP ViT-B/32 fine-tuned for typography) |
| Dim. reduction | PCA + UMAP (spectral init) via `umap-learn` |
| Hosting | Hugging Face Spaces (static SDK) |

## Credits

- **[FontCLIP](https://github.com/kinit-sk/FontCLIP)** β€” Typography-aware CLIP model by KInIT
- **[IDEO Font Map](https://medium.com/ideo-stories/organizing-the-world-of-fonts-with-ai-7d9e49ff2b25)** β€” Original inspiration by Kevin Ho (2017)
- **[Google Fonts](https://fonts.google.com)** β€” Font catalog
- **[UMAP](https://umap-learn.readthedocs.io/)** β€” Dimensionality reduction by Leland McInnes

## License

MIT