Skip to content
>sdcb.ai
New · 2026.10React · TypeScript · Cloudflare Pages

Forgot how to split a character?Look it up.

Years of typing Wubi 86, and characters like 鼎 still trip me up. Most lookup sites just give you a code — I wanted the why: which roots, which strokes each root covers, and whether the last letter is an ID code.

胜
shēng / shèng
etgg

Moon + pie + the top of 青 — only 3 roots, so a final-stroke ID code is added: last stroke heng, left-right → 11 → G.

Microsoft Wubi entries
534.1k
characters
27.5k
with stroke diagrams
9.6k
JSON shards, loaded on demand
2.7k

Features

More than a code lookup

Sentences

How few keys can “今天吃什么” take?

Whole sentences aren’t in the dictionary, and a rule-derived code won’t type them. Instead the sentence is split into real dictionary entries; each chunk costs its code length plus the commit key, and DP finds the cheapest split.

src/lib/plan.ts
// '' = auto commit, ' ' = space, '2'..'9' = pick
function commitKey(code: string, rank: number, n: number) {
  if (rank > 9) return null
  if (code.length === 4 && n === 1) return ''
  return rank === 1 ? ' ' : String(rank)
}

Recommended

First candidates only — nothing to memorise

14keys
今天wygd␣
吃ktn␣
什么wftc␣

Optimised

Digit picks allowed — fewer keys

10keys
今天wygd␣
吃什么kwtc4
See the full breakdown
scripts/extract-ms-lex.mjs
// Record layout: u16 len | u16 weight | u16 codeLen | 4 x u16 code | utf16le word | u16 0
let p = 0xa8
while (p + 2 <= b.length) {
  const len = b.readUInt16LE(p)
  if (len === 0) break
  const weight = b.readUInt16LE(p + 2)
  const codeLen = b.readUInt16LE(p + 4)
  const code = b.toString('utf16le', p + 6, p + 6 + codeLen * 2)
  const word = b.toString('utf16le', p + 14, p + len - 2)
  lines.push(`${code}\t${word}\t${weight}`)
  p += len
}

Data

Identical to the IME on your PC

Wubi 86 tables floating around the web disagree on short codes, phrases and candidate order. These codes are parsed straight from ChsWubiNew.lex that ships with Windows — undocumented but simple: 530k+ records, and sorting by weight gives the exact candidate order.

So when the site says “2nd candidate, press 2”, that is exactly what you press.

Algorithm

The hard part: matching strokes to roots

Root splits come from my earlier search-wubi project and stroke vectors from Make Me a Hanzi — but nothing says which stroke belongs to which root, except for ~2,000 hand-labelled characters. An algorithm fills in the rest:

  1. 01
    2.2k

    Hand-labelled

    Characters in search-wubi that already map strokes to roots.

  2. 02
    Learn

    Root → stroke pattern

    Learn which stroke-type sequences each root is usually made of.

  3. 03
    6k

    Strict backtracking

    Backtrack the stroke sequence onto the roots; score ties statistically.

  4. 04
    496

    Loose + edit distance

    Allow reordering within a chunk, then fall back to edit distance — flagged as “inferred”.

Validated against the labelled set, ~95% match exactly. 8.7k characters now have stroke-level diagrams, covering nearly all common ones.

Next time you forget, just look it up

Works on phone and desktop, no sign-up, no ads. Open source under AGPL-3.0 — spot a wrong split? Open an issue.

Related writing