Hangul jamo composition

한글의 초성·중성·종성

A Hangul syllable block is composed of an initial consonant, a vowel, and an optional final consonant, combined by a fixed rule — one Unicode encodes directly as arithmetic.

Also known as: 자모 조합Hangul syllable block초중종성
···
html
<div class="stage">
  <div class="jamo-row">
    <span class="jamo" id="j1">ㅎ</span>
    <span class="jamo" id="j2">ㅏ</span>
    <span class="jamo" id="j3">ㄴ</span>
  </div>
  <div class="syllable" id="syl">한</div>
  <div class="formula" id="formula">0xAC00 + (18×21+0)×28+4 = 한</div>
</div>
css
.stage{display:grid;gap:10px;justify-items:center}
.jamo-row{display:flex;gap:10px;height:9vmin;align-items:center}
.jamo{font:700 6vmin/1 -apple-system,system-ui,"Apple SD Gothic Neo",sans-serif;color:var(--accent-2);transition:opacity .5s ease, transform .5s ease}
.jamo.merged{opacity:0;transform:translateY(14px) scale(.5)}
.syllable{font:800 15vmin/1 -apple-system,system-ui,"Apple SD Gothic Neo",sans-serif;color:var(--fg);opacity:0;transform:scale(.6);transition:opacity .5s ease, transform .5s ease}
.syllable.show{opacity:1;transform:scale(1)}
.formula{font:10px ui-monospace,Menlo,monospace;color:var(--muted);text-align:center;min-height:14px}
js
const CHO = ['ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ','ㅆ','ㅇ','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'];
const JUNG = ['ㅏ','ㅐ','ㅑ','ㅒ','ㅓ','ㅔ','ㅕ','ㅖ','ㅗ','ㅘ','ㅙ','ㅚ','ㅛ','ㅜ','ㅝ','ㅞ','ㅟ','ㅠ','ㅡ','ㅢ','ㅣ'];
const JONG = ['','ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄼ','ㄽ','ㄾ','ㄿ','ㅀ','ㅁ','ㅂ','ㅄ','ㅅ','ㅆ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'];
const words = [
  { cho: 'ㅎ', jung: 'ㅏ', jong: 'ㄴ' },
  { cho: 'ㄱ', jung: 'ㅡ', jong: '' },
  { cho: 'ㅅ', jung: 'ㅐ', jong: 'ㄱ' },
];
function compose(w){
  const ci = CHO.indexOf(w.cho), vi = JUNG.indexOf(w.jung), fi = Math.max(JONG.indexOf(w.jong), 0);
  const code = 0xAC00 + (ci * 21 + vi) * 28 + fi;
  return { char: String.fromCharCode(code), code, ci, vi, fi };
}
const j1 = document.getElementById('j1');
const j2 = document.getElementById('j2');
const j3 = document.getElementById('j3');
const syl = document.getElementById('syl');
const formula = document.getElementById('formula');
let i = 0;
function step(){
  const w = words[i % words.length];
  const r = compose(w);
  j1.textContent = w.cho; j2.textContent = w.jung; j3.textContent = w.jong || '·';
  j1.classList.remove('merged'); j2.classList.remove('merged'); j3.classList.remove('merged');
  syl.classList.remove('show');
  syl.textContent = r.char;
  formula.textContent = '0xAC00 + (' + r.ci + '×21+' + r.vi + ')×28+' + r.fi + ' = ' + r.char;
  setTimeout(function(){
    j1.classList.add('merged'); j2.classList.add('merged'); j3.classList.add('merged');
    syl.classList.add('show');
  }, 700);
  i++;
}
step();
setInterval(step, 2400);

King Sejong's Hunminjeongeum gave consonants and vowels their own independent letterforms, then designed a rule for stacking them — an initial consonant, a medial vowel, and an optional final consonant (batchim) — into a single square-ish block per syllable. Instead of stringing letters horizontally like the Latin alphabet, Hangul stacks them within one syllable's footprint.

This composition rule is baked directly into Unicode's precomposed Hangul block (U+AC00–U+D7A3, 11,172 characters). Given an index into 19 initials (ㄱ–ㅎ), 21 medials (ㅏ–ㅣ), and 28 finals (including "none"), the formula `0xAC00 + (initialIndex × 21 + medialIndex) × 28 + finalIndex` gives the exact code point — and running it backwards decomposes any syllable into its three jamo.

That arithmetic is what makes jamo-level search (typing "ㄱㅅㅎ" to match "감사합니다"), dual/triple-layout Korean keyboards, and jamo-decomposition animation easier than in most writing systems. The demo decomposes "한" into initial ㅎ, medial ㅏ, final ㄴ, then actually computes the formula to recombine them.

When to use

Reach for this formula when you need to manipulate syllables programmatically — jamo-based search, IME logic, or debugging font rendering.