Vol. 01
2026.04.10
2026.04.09 ZENN // TECH

数式によるフォントジェネレーターを作った話

ORIGIN: ZENN.DEV READ ORIGINAL

はじめに

どうも、えなどりです。サラリーマンをしながら、隙間時間でWebサービスやアプリを作っています。

コードはマジで書けません。アイデアだけで生きています。

スタンスはシンプルで、「発想と意思決定は自分、実装はAI」。ClaudeやGeminiを相棒に、自分が欲しいと思ったものを作り続けています。※毎回言ってるの大丈夫そ?Claudeさんや。

さて、今回作ったのは TypePlot(タイポプロット) という、数式でフォントを作れるWebサービスです。

https://typeplot.ena-dri.dev/


なぜ数式でフォントなのか

きっかけはふとした思いつきでした。

「数学の関数って、グラフにすると綺麗な形になるじゃん。それを文字に使ったらどうなるんだろう?」

Geminiに調べてもらったところ、思ったより奥が深くて面白そう。sin波やリサージュ図形はそれ自体がすでに美しい。それを文字のグリフに転用する、という発想です。

AIでフォント作るっていうのが最近多いそうで?こちらは完全に人の手だけ、それも手書きじゃなくて数字1つで攻めるというやり方です。

既存ツールと比較するとこんな感じ:

ツール 特徴
Glyphs, RoboFont プロ向け。マウスで描く
Metaflop 数式ベースだが固定パラメータ・英字のみ
TypePlot 任意の数式を入力でき、ひらがな・カタカナにも対応

「任意の数式でひらがなまで作れる」というサービスは、調査した限り競合が存在しないご様子。ひらがなとかカタカナまで含めるとエライコッチャだもんね…

さらに、数式によるフォント生成は決定論的という強みがあります。パラメータさえ保存しておけば、誰でも同じフォントを再現・リミックスできる。これがギャラリー機能と相性抜群で、「このフォントどうやって作ったの?」→ パラメータを見れば全部わかる、という体験が作れます。


技術的な面白さ

パラメトリック方程式でグリフを定義する

フォントの文字(グリフ)は、通常はベジェ曲線で定義されます。Typeplotでは、その曲線を数式で生成します。

x(t), y(t) → t を 0〜1 で動かして点群を生成
→ fit-curve でベジェ近似(cubic bezier列)
→ opentype.js でTTFバイナリに変換してダウンロード

フォント座標系は unitsPerEm=1000x=[0,600]y=[0,700](ベースラインがY=0)で定義しています。

ユーザーはリストから21種のプリセットを選んでスライダーを動かすか、数式を直打ちするかで文字の形を決められます。スライダーだけで遊んでも面白いし、数学が好きな人は数式を自分で入力して思い通りの形を作れる。間口は広く、奥は深く、という設計です。

dirSine — 方向認識 sin 波

開発中に一番面白かった実装がこれです。

通常のsin波は常にX方向に振動するので、斜めのストロークに適用すると横波になってしまい、文字の形が破綻します。

「Aの斜め線をsin波にしたい」なら、斜めの方向に沿って振動しないといけない。そこで考えたのが dirSine(方向認識sin波) です。

// ストロークの方向ベクトルを計算
const dx = x1 - x0, dy = y1 - y0;
const len = Math.sqrt(dx*dx + dy*dy);

// 単位接線ベクトル
const tx = dx/len, ty = dy/len;
// 単位法線ベクトル(接線を90°回転)
const nx = -ty, ny = tx;

// 接線方向に進みながら、法線方向にsin波で振動
x(t) = x0 + t*len*tx + A*sin(2πft+φ)*nx
y(t) = y0 + t*len*ty + A*sin(2πft+φ)*ny

これで斜め方向の縦棒にも綺麗な波が描けます。「うねうね」テーマで全文字に適用したときの見た目は、なかなかカオスで楽しいです。

実装したプリセット関数一覧

刺さる人に刺さってほしいので、プリセットの名前からして本格的にしました。

カテゴリ プリセット
基本 直線・楕円・2次ベジェ・3次ベジェ
三角関数 sin波・cos波・タンジェント・リサージュ
方向対応 dirSine(方向認識sin波)
フーリエ フーリエ級数(3次合成)
代数曲線 放物線・双曲線
特殊曲線 バラ曲線・カージオイド・レムニスケート
周期曲線 サイクロイド・アストロイド・エピサイクロイド・ハイポサイクロイド
螺旋 アルキメデスの螺旋・対数螺旋

ドロップダウンにこれが並んでいるだけで、「おっ」てなると思っています。

ひらがな・カタカナ対応

日本語フォントジェネレーターは(調査した限り)競合がいないので、ここは差別化の核として力を入れました。

清音46文字×2をベースに定義し、濁点・半濁点・小文字カナは自動生成するヘルパー関数を作りました。

// 清音グリフに濁点2点を追加
function dakuten(base: GlyphDef, char: string): GlyphDef

// 清音グリフに半濁点の丸を追加
function handakuten(base: GlyphDef, char: string): GlyphDef

// 0.7倍スケール・中央寄せで小文字カナを生成
function smallKana(base: GlyphDef, char: string): GlyphDef

「ぱぴぷぺぽ」は「はひふへほ」の定義 + handakuten() で自動生成されます。定義を書かなくて済む。おかげで合計222文字をカバーできました。

字形の完成度はまだ荒削りですが、「数式で改変するためのベース」として使えます。


開発プロセス

Claude Code との協働

全7フェーズに分割して実装しました。

Phase 内容
1 基本エディタ・TTF生成
2 プリセット拡充(19種)
3 テーマ機能・JSON保存
4 DrawingCanvas・dirSine
5 ひらがな・カタカナ・TTFインポート
6 濁点・半濁点・自由描画タブ・UX改善
7 Vercelデプロイ・Supabaseギャラリー

「仕様窓(Claude / Gemini)」と「実装窓(Claude Code)」を分けて使っています。仕様を詰める会話と実装する会話を混ぜないことで、コンテキストが汚れません。
※毎回言ってる気がするぞClaudeさん。

フェーズ完了ごとに STATE_Ph*.md として状態を記録し、次のフェーズの冒頭で読ませる運用です。コンテキスト上限対策として有効でした。
仕様窓とのやり取りとも有用だったり。

詰まったポイントと解決策

  1. TTFのY軸が逆転glyph.getPath() は画面座標系(Y下向き)を返すため、glyph.path.commands を直接参照することで解決
  2. 6と9が逆に見える → 楕円の媒介変数 tStart/tEnd の設定ミス。楕円は t=0 が右端、t=0.5 が左端
  3. ひらがな選択でクラッシュ → 旧 localStorage データとの互換性問題。{ ...DEFAULT_GLYPHS, ...saved.glyphs } でマージして解決
  4. 「うねうね」テーマで全文字が横線になる → 通常のsineは水平方向固定のため dirSine を実装して解決

デモ・使い方

基本の使い方

  1. 左パネルで文字を選択(A〜Z / a〜z / 0〜9 / ひらがな / カタカナ)
  2. ストロークにプリセットを選んでパラメータを調整
  3. 右パネルでリアルタイムプレビュー
  4. 「TTFをダウンロード」で完成

おまけ:このサービスのロゴもTypeplotで作りました

サービス名「Typeplot」と「Gallery」の文字は、Typeplot自身で作ったフォントで表示されています。dirSineと楕円を組み合わせたカリグラフィ風フォントです。

自分のサービスを自分のサービスで作ったフォントで表示する、最高のドッグフーディングです。


おわりに

このプロジェクトを通じて改めて感じたのは、アイデアの組み合わせ方が全てだということです。

数学の関数もフォントも既存のものです。でも「数学の関数でフォントを作る」「しかもひらがなまで対応する」という組み合わせは、調べた限り誰もやっていませんでした。漏れがあったらすまん。正論は認める。

難しく考えなくても、スライダーを動かすだけで自分だけのフォントができます。数学が得意な人は数式を直打ちしてみてください。作ったフォントはギャラリーに投稿してもらえると嬉しいです。

今後の展望:

  • フーリエ係数の自動計算(手書き → 数式に変換)
  • ギャラリーの充実
  • ひらがな字形の改善

ぜひ遊んでみてください!

https://typeplot.ena-dri.dev/

Gallery機能もあるので、作ったフォントは是非Galleryに登録してみてほしい!