How Emoji Rendering Works Across Platforms: Font Stacks and Fallbacks

How EmojiEmoji
Từ tiếng Nhật (絵文字) có nghĩa là 'ký tự hình ảnh' — các ký hiệu đồ họa nhỏ dùng trong giao tiếp kỹ thuật số để diễn đạt ý tưởng, cảm xúc và sự vật.
Rendering Works Across Platforms

Open the same message on macOS, Windows, and Android and the 🍕 pizza emoji will look noticeably different on each. That's not a bug — it's by design. Emoji are part of the Unicode standardUnicode Standard
Hệ thống mã hóa ký tự đầy đủ do Unicode Consortium duy trì, định nghĩa các ký tự, thuộc tính, thuật toán và dạng mã hóa.
, which specifies which characters exist and what they mean, but leaves the visual design entirely up to platform vendors.

Understanding how rendering works under the hood helps you build apps that look good everywhere and degrade gracefully when an emoji is not available.

The Font Stack for Emoji

When the operating system needs to render a character, it walks a font stack — an ordered list of fonts — until it finds a glyph for that code point. Emoji fonts are typically placed near the end of the stack, after all text fonts.

System Emoji Fonts

Platform Font Format
macOS / iOS Apple Color EmojiColor Emoji
Emoji đầy màu sắc được hiển thị bằng hình ảnh bitmap hoặc đồ họa vector màu, trái ngược với cách hiển thị đơn sắc kiểu văn bản.
sbix (PNG bitmaps)
Windows Segoe UI Emoji COLR/CPALCOLR/CPAL (COLR)
Bảng font màu OpenType định nghĩa emoji dưới dạng các hình dạng vector được xếp lớp với bảng màu, được Windows và Chrome sử dụng.
v0 (vector)
Android Noto Color Emoji CBDT/CBLCCBDT/CBLC (CBDT)
Color Bitmap Data Table và Color Bitmap Location Table — các bảng OpenType dùng để nhúng emoji bitmap màu vào trong các tệp font.
(PNG bitmaps)
Linux Noto Color Emoji (common) CBDT/CBLC or COLRv1
ChromeOS Noto Color Emoji CBDT/CBLC

The font format determines rendering quality at different sizes. COLR-based fonts scale cleanly as vectors; bitmap-based fonts (sbix, CBDT) look sharp at their designed resolution but can appear blurry when scaled up significantly.

CSS Font Stack

In web applications, you can hint at emoji fonts via CSS:

body {
  font-family:
    /* System UI font first */
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    /* Emoji fonts — browser picks available one */
    "Apple Color Emoji",
    "Segoe UI Emoji",
    "Noto Color Emoji",
    sans-serif;
}

Browsers handle emoji rendering semi-automatically. Even without explicit emoji fonts in the stack, the browser falls back to the OS emoji fontEmoji Font
Tệp font kỹ thuật số chứa các thiết kế glyph emoji màu, sử dụng các công nghệ như COLR, CBDT, SVG hoặc sbix để hiển thị.
for characters that have no glyph in the active text font.

How the Rendering Pipeline Works

1. UnicodeUnicode
Tiêu chuẩn mã hóa ký tự phổ quát gán một số duy nhất cho mỗi ký tự trong tất cả hệ thống chữ viết và bộ ký hiệu, bao gồm cả emoji.
Code Point Resolution

The text string arrives as a sequence of Unicode code points. For ZWJZero Width Joiner (ZWJ)
Ký tự Unicode vô hình (U+200D) dùng để ghép nhiều emoji thành một emoji tổng hợp, chẳng hạn kết hợp người và vật thể thành emoji nghề nghiệp.
sequences like 👨‍👩‍👧‍👦 (family), the shaping engine must recognize the full sequence before deciding which glyph to use.

2. Shaping

A text shaping library (HarfBuzz on most platforms; Core Text on Apple) processes the sequence. For emoji:

  • It checks if the font supports the full ZWJ sequence as a ligature
  • If not, it falls back to rendering individual components
  • Variation selectors (U+FE0F for emoji, U+FE0E for text) influence which glyph is chosen

3. Glyph Selection

The shaper queries the font's GSUB (Glyph Substitution) table or a platform-specific lookup to find the appropriate glyph or glyph sequence.

4. Rasterization

For bitmap fonts (sbix, CBDT), the engine picks the closest pre-rendered PNG at a size matching the requested display size. For vector fonts (COLR), it renders the layered vector paths with their associated colors.

Platform Visual Differences

The same emoji character can differ in:

  • Shape: 😂 on Apple has distinctive teardrop placement vsVariation Selector (VS)
    Các ký tự Unicode (VS-15 U+FE0E và VS-16 U+FE0F) xác định xem một ký tự được hiển thị dưới dạng văn bản (đơn sắc) hay emoji (có màu).
    . Google's version
  • Color palette: 🔥 fire emoji ranges from yellow-orange (Apple) to red-heavy (Samsung)
  • Detail level: 🍣 sushi on Apple shows realistic nigiri; older Android versions showed a cartoon version
  • Gender and skin tone rendering: Some platforms differ in default presentation for gender-neutral sequences

Checking Platform Differences

To inspect how a specific emoji renders on different platforms, compare the SVG/PNG source files:

# Noto Emoji (Google) is open source
git clone https://github.com/googlefonts/noto-emoji
ls noto-emoji/png/128/  # PNG files named by code point

# TwemojiTwemoji
Bộ emoji mã nguồn mở ban đầu được Twitter tạo ra, cung cấp các tài nguyên emoji SVG và PNG có thể dùng trong bất kỳ dự án nào.
(Twitter/X) is also open source # Files are named by hex code point, e.g. 1f602.svg for 😂

Fallback Rendering: When an Emoji Is Not Supported

When a platform does not have a glyph for a code point or sequence, it falls back in one of these ways:

  1. Tofu (▯): A blank rectangle, the classic missing glyph indicator
  2. Component decomposition: A ZWJ sequence like 🧑‍🚀 (astronaut) breaks into 🧑 + 🚀
  3. Previous version rendering: An older, less detailed version of the character
  4. Text presentationText Presentation
    Cách hiển thị một ký tự dưới dạng ký hiệu văn bản đơn sắc, hoặc theo mặc định hoặc khi áp dụng Variation Selector-15.
    : The character renders as a symbol without color

Detecting Support in JavaScript

function supportsEmoji(emoji) {
  const canvas = document.createElement('canvas');
  canvas.width = canvas.height = 1;
  const ctx = canvas.getContext('2d');

  // Draw a known unsupported character first (baseline)
  ctx.fillText('\uD83E\uDD37', -4, 4); // 🤷 as baseline
  const baseline = ctx.getImageData(0, 0, 1, 1).data;

  // Clear and draw the emoji we're testing
  ctx.clearRect(0, 0, 1, 1);
  ctx.fillText(emoji, -4, 4);
  const tested = ctx.getImageData(0, 0, 1, 1).data;

  // If pixel data differs, the emoji is likely rendered
  return baseline[0] !== tested[0] || baseline[1] !== tested[1];
}

console.log(supportsEmoji('🥹')); // true on recent platforms
console.log(supportsEmoji('\u{1FAE0}')); // newer emoji, may be false on older OS

Note: this canvas technique is not 100% reliable across all browsers, but works well enough for graceful degradation decisions.

Web Rendering Considerations

Variation Selector-16

Many symbols need an explicit U+FE0F (VS16) to render as emoji in browsers. Without it, they may appear as monochrome text glyphs:

<!-- May render as text ☎ -->
<span>&#x260E;</span>

<!-- Forces emoji presentationEmoji Presentation
Cách hiển thị mặc định của một ký tự dưới dạng glyph emoji có màu, hoặc vốn có hoặc được kích hoạt bởi Variation Selector-16.
☎️ --> <span>&#x260E;&#xFE0F;</span>

Emoji and Line Height

Color emoji fonts often have metrics that push line height higher than your text font. Apply consistent sizing:

.emoji {
  font-size: 1em;
  line-height: 1;
  vertical-align: -0.1em; /* fine-tune per font */
}

Using Image Fallbacks

For consistent cross-platform display (common in chat applications), replace emoji with images from a standardized set:

import twemoji from 'twemoji';

// Replace all emoji in element with Twemoji SVG images
twemoji.parse(document.body, {
  folder: 'svg',
  ext: '.svg',
  base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/'
});

This ensures pixel-perfect consistency but increases DOM complexity and disables native copy-paste of text.

Native App Considerations

iOS and macOS

Apple's emoji font is private and ships with the OS. You cannot substitute it. All emoji rendering goes through Core Text, which handles ZWJ sequences, skin tone modifiers, and variation selectors automatically.

Android

Android ships Noto Color Emoji. Older Android versions (pre-4.4) had very limited emoji support. Apps targeting older API levels should either: - Use a bundled emoji font via EmojiCompat (Jetpack library) - Fall back to image assets

// Jetpack EmojiCompat for backward compatibility
val config = BundledEmojiCompatConfig(context)
EmojiCompat.init(config)

// In layout XML
<androidx.emoji2.widget.EmojiTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello 👋" />

Testing Your Rendering

A practical testing matrix for emoji rendering:

  1. macOS Safari — Apple Color Emoji, WebKit rendering
  2. Windows Chrome — Segoe UI Emoji, Blink rendering
  3. Android Chrome — Noto Color Emoji, Blink rendering
  4. iOS Safari — Apple Color Emoji, WebKit rendering
  5. Linux Firefox — Noto (or system), Gecko rendering

Use browser developer tools to inspect which font serves a given glyph: in Chrome DevTools, the Computed panel shows the resolved font for selected text.

Explore More on EmojiFYI

Công cụ liên quan

🔀 So sánh nền tảng So sánh nền tảng
So sánh cách emoji hiển thị trên Apple, Google, Samsung, Microsoft và nhiều hơn nữa. Xem sự khác biệt trực quan cạnh nhau.
🔍 Trình phân tích chuỗi Trình phân tích chuỗi
Giải mã chuỗi ZWJ, modifier tông màu da, chuỗi phím và cặp cờ thành các thành phần riêng lẻ.

Thuật ngữ

CBDT/CBLC (CBDT) CBDT/CBLC (CBDT)
Color Bitmap Data Table và Color Bitmap Location Table — các bảng OpenType dùng để nhúng emoji bitmap màu vào trong các tệp font.
Color Emoji Color Emoji
Emoji đầy màu sắc được hiển thị bằng hình ảnh bitmap hoặc đồ họa vector màu, trái ngược với cách hiển thị đơn sắc kiểu văn bản.
COLR/CPAL (COLR) COLR/CPAL (COLR)
Bảng font màu OpenType định nghĩa emoji dưới dạng các hình dạng vector được xếp lớp với bảng màu, được Windows và Chrome sử dụng.
Điểm mã Điểm mã
Giá trị số duy nhất được gán cho mỗi ký tự trong tiêu chuẩn Unicode, được viết theo định dạng U+XXXX (ví dụ: U+1F600 cho 😀).
Emoji Emoji
Từ tiếng Nhật (絵文字) có nghĩa là 'ký tự hình ảnh' — các ký hiệu đồ họa nhỏ dùng trong giao tiếp kỹ thuật số để diễn đạt ý tưởng, …
Emoji Font Emoji Font
Tệp font kỹ thuật số chứa các thiết kế glyph emoji màu, sử dụng các công nghệ như COLR, CBDT, SVG hoặc sbix để hiển thị.
Emoji Presentation Emoji Presentation
Cách hiển thị mặc định của một ký tự dưới dạng glyph emoji có màu, hoặc vốn có hoặc được kích hoạt bởi Variation Selector-16.
Text Presentation Text Presentation
Cách hiển thị một ký tự dưới dạng ký hiệu văn bản đơn sắc, hoặc theo mặc định hoặc khi áp dụng Variation Selector-15.
Twemoji Twemoji
Bộ emoji mã nguồn mở ban đầu được Twitter tạo ra, cung cấp các tài nguyên emoji SVG và PNG có thể dùng trong bất kỳ dự án nào.
Unicode Unicode
Tiêu chuẩn mã hóa ký tự phổ quát gán một số duy nhất cho mỗi ký tự trong tất cả hệ thống chữ viết và bộ ký hiệu, bao gồm …
Unicode Standard Unicode Standard
Hệ thống mã hóa ký tự đầy đủ do Unicode Consortium duy trì, định nghĩa các ký tự, thuộc tính, thuật toán và dạng mã hóa.
Variation Selector (VS) Variation Selector (VS)
Các ký tự Unicode (VS-15 U+FE0E và VS-16 U+FE0F) xác định xem một ký tự được hiển thị dưới dạng văn bản (đơn sắc) hay emoji (có màu).
Zero Width Joiner (ZWJ) Zero Width Joiner (ZWJ)
Ký tự Unicode vô hình (U+200D) dùng để ghép nhiều emoji thành một emoji tổng hợp, chẳng hạn kết hợp người và vật thể thành emoji nghề nghiệp.

Bài viết liên quan