Forgemoji

すべてのコンテンツ制作者が知っておくべき絵文字エンコーディングの知識

絵文字は画像ではなくテキストです。コードポイント、サロゲートペア、ZWJ シーケンス、異体字セレクタを持ちます。多くのコンテンツ制作者はそれらを不透明なグリフとして扱います。エンコーディングが重要であり、バグも実在します。

Lois Chen

Lois Chen·絵文字文化リサーチャー + プラットフォーム別ガイドライター·2026年6月20日

Hand-drawn infographic: emoji encoding and cross-platform rendering

多くのコンテンツ制作者は絵文字を不透明な画像として扱います。ピッカーをクリックし、適切な顔を選び、送信し、受信側のデバイスがそれぞれのやり方でレンダリングする。これは 95% のケースで機能します。残り 5% のケース、文字化け、分断されたクラスタ、絵文字の代わりに表示されるクエスチョンマーク、プラットフォーム固有のレンダリングは、絵文字がテキストであり、エンコーディングこそが本質であるために発生します。

絵文字は画像ではなくテキストである

絵文字について最も重要な事実は、それらが画像ではなくテキストであるという点です。すべての絵文字には Unicode のコードポイント (Unicode Consortium によって割り当てられた番号) があり、そのコードポイントはテキストストリームの一部として保存されます。😂 を含むメッセージを送信するとき、メッセージには実際にはコードポイント U+1F602 が含まれており、受信側のデバイスはそれぞれのグリフでそれをレンダリングします。

同じコードポイントがプラットフォームごとに異なるレンダリングになるのは、各プラットフォームが独自のグリフを描いているためです。😂 は iOS ではフラットな黄色い顔、Google ではブロブ、Samsung では 3D レンダリング、Twitter 画像プロキシでは高コントラストのモノクロになります。コードポイントは同じで、描画はプラットフォーム固有です。これはラテン文字とまったく同じモデルです。文字「A」のコードポイントは U+0041 であり、「A」がどう見えるかはフォント次第です。

基本:コードポイントと UTF-8

すべての Unicode 文字にはコードポイントがあり、U+ 接頭辞付きの 16 進数で表記されます。絵文字のコードポイントは補助多言語面 (Supplementary Multilingual Plane, SMP) にあり、つまり U+FFFF より上に位置します。ここでエンコーディングが興味深い領域に入ります。

UTF-8 (現在 Web 上で支配的なエンコーディング) では、各コードポイントは 1〜4 バイトでエンコードされます。絵文字は常に 4 バイトです。UTF-16 (JavaScript 文字列と Windows が使用するエンコーディング) では、U+FFFF を超えるコードポイントはサロゲートペア (surrogate pair) としてエンコードされます。これは 1 つのコードポイントを表す 2 つの 16 ビットコードユニットです。これが、絵文字が JavaScript において 1 文字ではなく 2 文字の長さを持つ理由です。"😂".length === 2 となります。これにより、文字カウントを行う多くのフロントエンドコードがつまずきます。

よく使われる絵文字のコードポイント

絵文字コードポイントUTF-8 バイトUTF-16 コードユニット
😂U+1F60242
❤️U+2764 U+FE0F64
👨‍👩‍👧U+1F468 U+200D U+1F469 U+200D U+1F4671710
🇺🇸U+1F1FA U+1F1F884

最後の行は興味深いです。🇺🇸 (アメリカ合衆国の国旗) は 2 つの地域インジケータ記号のコードポイント (U+1F1FA が「U」、U+1F1F8 が「S」) を組み合わせて国旗グリフにしたものです。国旗自体に専用のコードポイントは存在せず、そのペアから計算されます。これが、リージョンインジケータに対応するプラットフォームで「US」と入力すると 🇺🇸 が得られる一方で、内部データは 1 つではなく 2 つのコードポイントである理由です。

一部の絵文字が複数のコードポイントで構成される理由

一部の絵文字は単一のコードポイントです (😂 は U+1F602 のみ)。一部はシーケンスです。👨‍👩‍👧 (家族: 男性、女性、女の子) は、ゼロ幅接合子 (U+200D) で連結された 5 つのコードポイントです:U+1F468 (男性) + U+200D (ZWJ) + U+1F469 (女性) + U+200D (ZWJ) + U+1F467 (女の子)。ZWJ はレンダラーへの指示です。両側のコードポイントを 1 つのグリフに結合せよ、という命令です。

これは強力な仕組みです。ユーザーは任意の構成の家系を組み立てられます。👨‍👩‍👧‍👦 (親 2 人、子 2 人) や 👨‍👨‍👧 (父 2 人、娘 1 人) も、コードポイントを順に打つだけで構成できます。たとえその特定の組み合わせをかつてレンダリングしたプラットフォームがなくても、結果は得られます。レンダリングプラットフォームが ZWJ シーケンスを認識するかどうかによって結果が決まります。最近のプラットフォームの多くは認識しますが、レンダリングにはばらつきがあり、古いプラットフォームでは絵文字が独立したグリフとして表示されることがあります。

サロゲートペアと JavaScript

サロゲートペアの問題は、JavaScript における絵文字関連バグの主要な発生源です。JavaScript の文字列は UTF-16 であり、U+FFFF を超えるコードポイントは 2 つの 16 ビットコードユニット (上位サロゲートと下位サロゲート) として保存されます。つまり "😂".length === 2 であり、1 ではありません。文字列には 2 つの UTF-16 コードユニットが含まれるためです。

最もよくあるバグは文字カウントです。素朴な message.length は UTF-16 コードユニットの数を返し、ユーザーが知覚する文字数ではありません。5 つの絵文字と 10 個の ASCII 文字を含むメッセージは、15 ではなく 20 という長さを返します。修正はスプレッド構文 ([...message].length) または Intl.Segmenter API を使うことで、いずれもコードポイントまたは書記素クラスタ (grapheme cluster) でカウントします。

異体字セレクタ

異体字セレクタ (Variation Selector) は、直前のコードポイントのレンダリングを変更するコードポイントです。最も一般的なのは U+FE0F (VARIATION SELECTOR-16, VS-16) で、文字スタイルと絵文字スタイルの両方を持つ文字に対して絵文字スタイルでのレンダリングを強制します。

最も分かりやすい例はハートです。❤ (U+2764) はデフォルトではテキストスタイルのハートグリフとしてレンダリングされます。厚く、濃い赤色で、プリンタのディングバットのような外見です。❤️ (U+2764 U+FE0F) は同じハートコードポイントに VS-16 を続けたもので、絵文字スタイルを強制します。明るく赤く、つやのあるハートグリフです。この 2 つは大きく異なる見え方をし、その違いは 1 つのコードポイントです。

これが、ピッカーで「heart」と入力したときと Unicode 名を手で打って運を天に任せてみたときの結果が異なる理由です。ピッカーは常に異体字セレクタを自動で付与します。生のコードポイントを手で打つ場合は付与されません。

スキントーン修飾子

Fitzpatrick スケールの絵文字修飾子 (U+1F3FB 〜 U+1F3FF) は、対応する絵文字の肌色を変更します。🏋️ (ウエイトリフター) は、U+1F3CB (ウエイトリフター) + U+FE0F (VS-16) + U+1F3FB (明るい肌色) の組み合わせです。修飾子を付けない場合はデフォルトの黄色い絵文字が使われ、修飾子を付けると特定の肌色でレンダリングされます。

重要なのは、スキントーン修飾子は明示的に修飾子の受け入れが設計された絵文字に対してのみ機能するという点です。修飾子に対応していない絵文字にスキントーン修飾子を付けても機能しません (修飾子は無視されます)。また、対応していない組み合わせの扱いはプラットフォーム間で一貫していません。Forgemoji ジェネレーターは Unicode 仕様に準拠しています。対応する組み合わせは肌色付きでレンダリングし、非対応はデフォルトの黄色を使います。

よくあるバグとその回避方法

  • データベースでの文字化け。ISO-8859-1 などの Unicode 以前のエンコーディングを期待するデータベースに絵文字を保存すると、データが破損します。スタック全体で UTF-8 を使用してください。
  • 長さ計算。素朴な文字カウントは、絵文字が多いコンテンツでユーザー知覚の長さを過小評価します。書記素クラスタ (grapheme cluster) を使ってカウントしてください。
  • 検索インデックス。検索エンジンが空白でトークナイズする場合、絵文字は周囲のテキストの一部としてインデックスされます。最近の検索エンジン (Elasticsearch、OpenSearch、Algolia) は正しいトークナイザーで絵文字を適切に扱えます。
  • アクセシビリティ。スクリーンリーダーは CLDR 短縮名を読み上げます。カスタム絵文字や AI 生成の絵文字では、その短縮名が誤っているか欠落している可能性があります。スクリーンリーダーでテストしてください。
  • データベースのストレージ制限。VARCHAR(255) はコードユニットをカウントし、文字をカウントするわけではありません。MySQL の utf8mb4 エンコーディングの VARCHAR(255) は 255 文字を保持できますが、バイト上限は 1 文字あたり 4 バイトのため、絵文字が多いコンテンツの実質的なストレージ予算は 1020 バイトです。計画的に設計してください。

実務的な要点

  • スタック全体で UTF-8 を使う。2026 年に Unicode 以前のエンコーディングを使う理由はもうない。
  • 長さに敏感的コード (データベースカラム、文字カウンター) では、UTF-16 コードユニットではなく書記素クラスタまたはコードポイントで計算する。
  • あなたのユーザーに重要なプラットフォームで絵文字レンダリングをテストする。iOS、Android、Windows はそれぞれレンダリングが異なる。
  • ZWJ シーケンスは古い端末でもテストする。レンダリングは 2018 年にも 2026 年にもまだ完全には一貫していない。
  • 異体字セレクタは重要。絵文字スタイルが欲しいなら VS-16 を付ける。不要なら付けない。

Forgemoji はクリーンな透過 PNG を出力します。エンコーディングはあなたの側で解決すべき問題ですが、エンジニアリングノートには ZWJ シーケンスに関する詳細な技術解説があります。

仕組みを見る →

出典

おすすめの関連記事

  • 絵文字はどう言語になったのか:Unicode シンボルから文化的な速記まで — コードポイントと名前が重要な理由
  • 絵文字アクセシビリティガイド:カスタム絵文字をすべての人に読みやすくする — エンコーディング知識を実務に活かす
  • 透過 PNG / GIF / WebP エクスポート対応の AI 絵文字ジェネレーターの作り方 — なぜエクスポート形式に違いが出るのか

出典

Source: Unicode 16.0 コア仕様 。 補助文字と絵文字 Unicode Consortium (2026 年 6 月確認)

Source: MDN 。 文字列長とサロゲートペア Mozilla Developer Network (2026 年 6 月確認)

Lois Chen

Lois Chen·コンテンツ編集者

確認日:2026年6月20日

執筆プロセス:ブログ記事は、一次的なプラットフォーム検証(Discord サーバー、Telegram グループ、TikTok)、r/discordapp や Telegram ステッカーコミュニティのヘビーユーザーへのインタビュー、毎週の Unicode リリースノートの確認をもとに執筆しています。各ガイドは少なくとも 1 名の編集者による技術的な正確性のレビューを受け、対象プラットフォームのルール変更時には更新されます。絵文字使用データは、公開されている Google Trends、UDF(Unicode 絵文字頻度)レポート、そして Forgemoji 自身の生成ログから収集しています。

出典:Forgemoji 内部編集チーム — 個別の執筆者プロフィールは「私たちについて」ページをご覧ください