tsubu is a small, dependency-free browser library that draws exactly one sentence for read-along and language learning: a dotted line under each segment, furigana that switch on and off without moving a character, and a highlight that follows the voice as a pure function of time.
tsubu 是一个很小、零依赖的浏览器库,只画一句话,用于跟读和语言学习:每段一条点状下划线,注音开关时任何一个字都不移动,高亮作为时间的纯函数跟着声音走。
tsubu は、音読や語学学習のために「一文だけ」を描く、依存ゼロの小さなブラウザライブラリです。文節ごとの点線の下線、文字を一切動かさずに切り替えられるふりがな、そして時刻の純関数として声に追従するハイライトを備えています。
Every frame is drawn from the time alone, by renderAt(t): the same page plays here and renders to video frame by frame. The sentence is drawn by tsubu with the appear preset; the field, the ink grain and the large reading follow the state tsubu reports for that time. The times are set to a beat, not measured from a recording.
每一帧都只由时间决定,由 renderAt(t) 画出:同一个页面既在这里播放,也能逐帧渲染成视频。句子由 tsubu 用 appear 预设绘制;底色、墨色米粒和大字读音都跟着 tsubu 在那个时刻报告的状态走。时间是按节拍定的,不是从录音里量的。
どのフレームも時刻だけから renderAt(t) で描かれます。同じページがここで再生され、そのままフレームごとに動画へも書き出せます。文は tsubu が appear プリセットで描き、背景の色面、墨色の米粒、大きな読みは、その時刻に tsubu が返す状態に従います。時刻は拍に合わせて決めたもので、録音から計測したものではありません。
Move over the sentence to read its furigana, with each segment's box and its time. On a phone, tap a word; with the keyboard, focus the sentence and use the arrow keys.
把指针移到句子上,就能看到注音、每一段的框和它的时间。手机上点一个词;用键盘时,先聚焦句子,再按方向键。
文の上にポインタを重ねると、ふりがな、各文節の枠、その時刻が見えます。スマートフォンでは語をタップ、キーボードでは文にフォーカスして矢印キーで移動します。
The sentence is drawn twice from the same HTML, once with furigana off and once with them on. The two layers line up exactly because switching furigana in tsubu moves nothing.
同一段 HTML 画了两遍,一遍不带注音,一遍带。两层完全对齐,因为在 tsubu 里开关注音什么都不会动。
同じ HTML を、ふりがななしとありで二度描いています。tsubu ではふりがなを切り替えても何も動かないので、二つの層はぴったり重なります。
<ruby> and switch on and off without moving a character, which a test measures in a real browser. Where ruby cannot be drawn, they read 雨(あめ).读音是 <ruby>,开关时任何一个字都不移动;有测试在真实浏览器里量过。画不出 ruby 的地方显示为 雨(あめ)。読みは <ruby> で、表示・非表示を切り替えても文字は一つも動きません。実際のブラウザで計測するテストがそれを保証します。ルビを描けない環境では 雨(あめ)と表示されます。t每一帧都是 t 的函数フレームは t の関数stateAt(t) is pure, and apply(root, t) draws what it returns. No timers, no transitions: live playback and frame-by-frame capture draw the same thing.stateAt(t) 是纯函数,apply(root, t) 把它的结果画出来。没有计时器、没有过渡:实时播放和逐帧截图画出来完全一样。stateAt(t) は純関数で、apply(root, t) はその結果を描きます。タイマーもトランジションもないので、リアルタイム再生とフレーム単位のキャプチャが同じ絵になります。checkContrast, which measures every state against WCAG-based thresholds.浅色页、深色页和叠在视频上的三套主题,按说话人上色,以及按 WCAG 阈值测量每个状态的 checkContrast。明るいページ、暗いページ、動画の上の三つのテーマ、話者ごとの配色、そして WCAG に基づくしきい値で全状態を計測する checkContrast。t. Vertical writing too.只在段与段之间、按禁则断行,各行长度接近,字号随容器变化,超长时按 t 翻页。也支持竖排。改行は文節の間だけ、禁則処理つき。行の長さは揃い、文字サイズはコンテナに追従し、長い文は t でページ送りされます。縦書きにも対応。appear, mask, blur and underline-draw, each drawn from the state at t, and revealing on the learner's own events.预设 appear、mask、blur、underline-draw,都由 t 时刻的状态画出;也能按学习者的操作揭示。プリセット appear、mask、blur、underline-draw はいずれも時刻 t の状態から描かれ、学習者の操作で明かすこともできます。What tsubu does not do: split text into segments, align audio, estimate times, play audio, draw a speech bubble or lay out more than one sentence. Those belong to the caller.
tsubu 不做的事:切分文本、对齐音频、估算时间、播放音频、画对话气泡、排多句话。这些由调用方负责。
tsubu がしないこと:テキストの分割、音声との整合、時刻の推定、音声の再生、吹き出しの描画、複数の文の配置。これらは呼び出し側の仕事です。