How Emoji Fonts Work: COLR, CBDT, sbix, and SVG Font Tables

How Emoji Fonts Work

Standard OpenType fonts store glyphs as monochrome vector outlines. Emoji fonts break this model completely — they need full-color, high-detail images at multiple sizes. Four different technical approaches have emerged to solve this problem, each with different trade-offs in quality, scalability, and platform compatibility.

The Core Challenge

A regular font glyph is a set of Bézier curves colored by the application. An emoji glyph needs:

  • Full RGB (or RGBA) color information
  • Multiple levels of detail at different sizes
  • Complex compositions (e.g., skin tones, hair colors)
  • Support for sequences of multiple code points

OpenType addresses this with four color font specifications, each stored in dedicated font tables.

COLR/CPALCOLR/CPAL (COLR)
레이어된 벡터 도형과 색상 팔레트로 이모지를 정의하는 OpenType 컬러 폰트 테이블.
: Layered Vector Glyphs

Used by: Segoe UI Emoji (Windows), Noto Color Emoji (recent versions), Twemoji Mozilla

COLR (Color Table) and CPAL (Color Palette Table) work together. A COLR glyph is composed of multiple layers, each a monochrome glyph from the font's regular outline table, filled with a color from the CPAL palette.

COLRv0 (Original)

The original COLR format (v0) supports flat fills only — no gradients, no composite modes.

Glyph "😀" decomposition in COLRv0:
  Layer 1: circle_outline  → color[0] = #FFCC33 (yellow)
  Layer 2: eyes_outline    → color[1] = #000000 (black)
  Layer 3: smile_outline   → color[2] = #000000 (black)
  Layer 4: cheeks_outline  → color[3] = #FF9966 (orange-pink)

COLRv1 (2021)

COLRv1 extends COLR with gradients, composite modes, variable fonts, and parametric color palettes. This enables:

  • Smooth gradients (linear, radial, sweep)
  • Blend modes (screen, multiply, etc.)
  • Font variation axes (including color axes)
# Inspect a COLR font with fonttools
pip install fonttools

from fontTools.ttLib import TTFont
font = TTFont("NotoColorEmoji-Regular.ttf")

# Check for COLR table
if "COLR" in font:
    colr = font["COLR"]
    print(f"COLR version: {colr.version}")
    # List glyphs with color definitions
    if colr.version == 1:
        for glyph_name in list(colr.table.BaseGlyphList.BaseGlyphPaintRecord)[:5]:
            print(glyph_name)

Advantages of COLR

  • Scalable: Vector-based, renders sharply at any size
  • Small file size: Shares glyph outlines across variations
  • Variable font compatible: Supports wght, ital, and custom axes
  • COLRv1: Supported in Chrome 98+, Firefox 105+, macOS Ventura+

CBDT/CBLCCBDT/CBLC (CBDT)
폰트에 비트맵 컬러 이모지 이미지를 임베딩하기 위한 OpenType 테이블.
: Bitmap Emoji

Used by: Noto Color Emoji (older versions), Android emoji font

CBDT (Color Bitmap Data Table) stores PNG or JPEG images directly in the font file. CBLC (Color Bitmap Location Table) provides an index of bitmap locations at different sizes.

CBLC structure:
  Size 16×16 → offset → CBDT PNG data
  Size 32×32 → offset → CBDT PNG data
  Size 64×64 → offset → CBDT PNG data
  Size 128×128 → offset → CBDT PNG data

Inspecting CBDT with fonttools

from fontTools.ttLib import TTFont
import io
from PIL import Image

font = TTFont("NotoColorEmoji.ttf")

# Extract the 128px bitmap for 😀
cblc = font["CBLC"]
cbdt = font["CBDT"]

# Find the glyph name for U+1F600
glyph_name = font.getBestCmap()[0x1F600]  # "emoji_u1f600"

# Access strike data (128px size)
for strike in cblc.strikes:
    if strike.bitmapSizeTable.ppemX == 128:
        if glyph_name in strike.indexSubTables[0].names:
            # Extract PNG bytes from CBDT
            bitmap_data = cbdt.strikeData[strike][glyph_name]
            img = Image.open(io.BytesIO(bitmap_data.data))
            img.save("grinning_face_128.png")
            break

Disadvantages of CBDT

  • Large file size: Noto Color Emoji is ~10MB because it stores PNGs at multiple sizes
  • Fixed resolution: Looks blurry at sizes not pre-baked into the font
  • No variable font support: Cannot animate or vary parameters

sbix: Apple's Bitmap Approach

Used by: Apple Color Emoji (macOS, iOS)

The sbix (Standard Bitmap Graphics) table is Apple's proprietary format. It stores PNG (or TIFF, JPEG) images per glyph, per size strike, similar to CBDT but with Apple-specific extensions.

sbix strikes (Apple Color Emoji):
  20px, 32px, 40px, 48px, 64px, 96px, 160px
  Each size contains PNG data per emoji glyph

Apple Color Emoji is not publicly distributable (it is part of macOS/iOS). You can inspect it on a Mac:

# Location of Apple Color Emoji on macOS
ls /System/Library/Fonts/Apple\ Color\ Emoji.ttc

# Extract info with fonttools
python3 -c "
from fontTools.ttLib import TTFont
font = TTFont('/System/Library/Fonts/Apple Color Emoji.ttc', fontNumber=0)
if 'sbix' in font:
    sbix = font['sbix']
    print(f'sbix strikes: {[s.ppem for s in sbix.strikes]}')
    print(f'Number of glyphs: {len(sbix.strikes[0].glyphs)}')
"

sbix Scalability

Apple augments sbix with vector outlines in the glyf table. The font renders the bitmap at small sizes and switches to a higher-resolution PNG at larger sizes. Some sbix glyphs also use dupe references to point to the same image in a different glyph.

SVG in OpenTypeSVG in OpenType (SVGinOT)
확장 가능한 컬러 이모지를 위해 SVG 문서를 OpenType 폰트 파일에 직접 임베딩하는 방법.

Used by: Older Firefox versions, some experimental fonts

The SVG table embeds SVG documents directly in the font, one per glyph or glyph range. This enables full SVG capabilities: gradients, filters, animations (in theory), and arbitrary paths.

from fontTools.ttLib import TTFont

font = TTFont("emoji-with-svg.ttf")
if "SVG " in font:
    svg_table = font["SVG "]
    for doc_list in svg_table.docList:
        svg_data, start_glyph_id, end_glyph_id = doc_list
        print(f"SVG for glyph IDs {start_glyph_id}–{end_glyph_id}")
        print(svg_data[:200])  # Print first 200 chars of SVG

SVG Table Status

Browser support for SVG-in-OpenType has narrowed. Firefox dropped it in favor of COLRv1. It is still available for specialized print applications (InDesign, Illustrator) that render color fonts via SVG.

Font Format Comparison

Format Scale File Size Browser Support Platform
COLRv0 Vector Small Excellent Windows, Linux, Web
COLRv1 Vector + gradients Small Chrome 98+, Firefox 105+ Windows, Linux, Web
CBDT/CBLC Bitmap Large (~10MB) Good Android, Web
sbix Bitmap Large Safari, Chrome macOS, iOS
SVG-OT Vector Medium Limited InDesign, legacy Firefox

Color Font Rendering in CSS

Modern browsers support color fonts via CSS @font-face without special declarations. The browser automatically selects the best color table it supports:

@font-face {
  font-family: "NotoColorEmoji";
  src: url("/fonts/NotoColorEmoji-Regular.ttf") format("truetype");
}

.emoji-display {
  font-family: "NotoColorEmoji", "Apple Color Emoji", "Segoe UI Emoji";
  font-size: 32px;
  /* No special color-font CSS needed */
}

For COLRv1 specifically, you can control the active palette with font-palette:

.emoji-dark {
  font-palette: dark;
}

.emoji-custom {
  font-palette: --my-palette;
}

@font-palette-values --my-palette {
  font-family: "NotoColorEmoji";
  base-palette: 0;
  override-colors: 0 #ffcc00, 1 #ff6600;
}

Variable Color Fonts

COLRv1 enables a new class of fonts: variable color fonts, where color parameters can be animated or controlled by CSS:

@supports (font-variation-settings: "RNDM" 0) {
  .animated-emoji {
    animation: color-shift 2s infinite;
  }

  @keyframes color-shift {
    from { font-variation-settings: "FILL" 0; }
    to   { font-variation-settings: "FILL" 1; }
  }
}

The Bungee Color font and experimental emoji fonts from Google demonstrate this capability.

Practical Implications for Developers

  1. Bundle size: If embedding an emoji font in a web app, COLRv1 (Noto COLRv1) is significantly smaller than CBDT Noto (~2MB vs변형 선택자 (VS)
    문자를 텍스트(단색) 또는 이모지(컬러) 표현으로 렌더링할지 결정하는 유니코드 문자.
    ~10MB)
  2. Rendering consistency: For cross-platform consistency, use a bundled web font rather than relying on OS system fonts
  3. Subsetting: Use pyftsubset (fonttools) to extract only the emoji you need:
pyftsubset NotoColorEmoji.ttf \
  --unicodes="U+1F600,U+1F601,U+1F602,U+1F604" \
  --output-file="emoji-subset.ttf"

Explore More on EmojiFYI

관련 도구

🔀 플랫폼 비교 플랫폼 비교
Apple, Google, Samsung, Microsoft 등 다양한 플랫폼에서 이모지가 어떻게 표시되는지 비교해 보세요. 시각적 차이를 나란히 확인할 수 있습니다.
🔍 시퀀스 분석기 시퀀스 분석기
ZWJ 시퀀스, 피부톤 수정자, 키캡 시퀀스, 국기 쌍을 개별 구성 요소로 분해합니다.

용어 사전 항목

이모지 이모지
일본어 '그림 문자(絵文字)'를 뜻하며, 디지털 커뮤니케이션에서 감정과 개념을 표현하는 작은 그래픽 기호.
오픈타입 오픈타입
여러 컬러 테이블 기술을 통해 컬러 이모지 렌더링을 지원하는 폰트 형식.
트웨모지 트웨모지
트위터가 만든 오픈소스 이모지 세트로, SVG와 PNG 에셋을 자유롭게 사용할 수 있습니다.
이모지 폰트 이모지 폰트
COLR, CBDT, SVG 등의 기술을 사용하여 컬러 이모지 글리프를 포함하는 디지털 폰트 파일.
비트맵 이모지 비트맵 이모지
폰트 파일 내에 고정 해상도의 픽셀 기반 이미지로 저장되는 이모지.
컬러 이모지 컬러 이모지
비트맵 이미지 또는 컬러 벡터 그래픽을 사용하여 렌더링되는 풀컬러 이모지.
코드 포인트 코드 포인트
유니코드 표준에서 각 문자에 부여되는 고유한 숫자 값으로, U+XXXX 형식으로 표기됩니다.
CBDT/CBLC (CBDT) CBDT/CBLC (CBDT)
폰트에 비트맵 컬러 이모지 이미지를 임베딩하기 위한 OpenType 테이블.
COLR/CPAL (COLR) COLR/CPAL (COLR)
레이어된 벡터 도형과 색상 팔레트로 이모지를 정의하는 OpenType 컬러 폰트 테이블.
SVG in OpenType (SVGinOT) SVG in OpenType (SVGinOT)
확장 가능한 컬러 이모지를 위해 SVG 문서를 OpenType 폰트 파일에 직접 임베딩하는 방법.

관련 이모지 카테고리

관련 이모지

관련 스토리