Skip to article
Get started / Concepts

Concepts

How the on-device engine, edge meaning search, caching and plan limits work together, and what stays private.

On this page

Emojisense is one platform with a few moving parts. This page explains how they work together, so you know what runs where, what it costs and what stays private.

The layers

Each search goes to the cheapest layer that can answer it well. The device answers almost everything. Only unsure queries go further, and every layer is optional except the first.

  1. On the deviceThe alias dictionary answers every keystroke: slang, names, typos, 11 languages. Works offline.p95 0.4 msfree
  2. Precomputed shardsStatic files with answers for frequent queries. One download per prefix, then local.optionalfree
  3. Edge meaning searchA Cloudflare Worker embeds the query with Workers AI and caches the answer in each data center.≈ 50 msmetered
Over a plan limit, search stays on the device and the shards. It never fails.

The on-device engine

The emojisense package is a search engine with no dependencies. It runs in browsers, Node, Deno, Bun, Workers and extensions. It loads data packs: JSON files with every emoji’s names, keywords, shortcodes and aliases in one language.

  • 1,914 emoji from Emoji 17.0. Skin-tone variants map to their base emoji, and the picker applies the person’s tone.
  • 11 languages. English always loads first, because it carries the shortcodes. Other languages load next to it.
  • Core and extension parts. The English core pack is about 178 KB compressed and renders first. The extension part (about 255 KB) adds more aliases and typos when the browser is idle.
  • Immutable files. A pack version never changes, so browsers and CDNs cache it for a year. A data update is a new pack version.

The engine normalizes the query (case, accents, width, emoji), matches the last word as a prefix while people type, forgives typos, and weights rare words higher than common ones. It reports a confidence from 0 to 1 with every answer. A keystroke takes 0.4 ms at the 95th percentile.

TypeScript
import { createEngine, loadPacks } from "emojisense";

const packs = await loadPacks({
  baseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
  locales: ["en", "tr"],
});
const engine = createEngine(packs);

const { results, confidence } = engine.search("jurassic park");
// results: [{ emoji: "🦖", id: "1F996", score: 0.9, source: "alias" }, …]

The engine is the same everywhere. The Swift SDK is a port with conformance tests against it, and Pack format is the specification for other ports.

When the edge is asked

The SDK asks the semantic layers only when the dictionary is unsure. The rule is shouldUseSemantic in the engine: a query with confidence under 0.6, or a phrase of two or more words with confidence under 0.9. The request waits for a 200 ms pause in typing, and a newer keystroke cancels it.

QueryOn the deviceConfidenceAsks the edge
fire🔥 🧑‍🚒 👨‍🚒1.00No
jurassic park🦖 🦟 🚙0.90No
congrats👏 ㊗️ 🥳0.85No
we just shipped🚚0.67Yes
kolay gelsin—0.00Yes

The table is computed when this page is built, with the English core pack. With the extension pack loaded, the engine knows more phrases and asks the edge less often.

The API is a Cloudflare Worker in more than 300 cities, at https://api.emojisense.com (HTTPS only). For a meaning search it embeds the query with Workers AI (bge-m3, 1,024 dimensions) and compares it with the vectors of every emoji. Each emoji has a vector of its English description, bundled into the Worker, and one of its description in the query’s language, loaded on that language’s first query. The emoji’s best match counts. The comparison takes a few milliseconds. The model call takes about 50 ms inside the Worker at the median.

The query and the emoji are always embedded by the same model at the same size. The pack manifest pins both, and the Worker refuses a mismatch.

How results merge

When meaning results arrive, the SDK merges them with the dictionary results by weighted reciprocal rank fusion. Dictionary results with a score of 0.9 or more stay pinned at the top in their order, so the list does not jump under the person’s cursor. The more confident the dictionary was, the more weight its results keep.

Each result has a source: alias for the dictionary, semantic for meaning search, custom for custom emoji and culture for the optional culture layer, which adds emoji for the culture and the moment after the top result.

Caching

  • Packs and shards are static files. They are free, cached for a year and never metered.
  • One shared edge cache. The Worker caches answers in each data center for up to a week. The cache key is the query text, locale, limit, mode, index version and a hash of the served data, so a data fix is never answered from old entries. It holds no key, app or origin, so every app warms the same cache for everyone. Custom emoji and culture are added after the cache, per request.
  • In the client, the semantic client keeps the last 200 answers in memory.
  • Reaction suggestions are never cached, because message text is private. For photos, only the model’s label (caption, reaction, keywords and proposed emoji) is cached, keyed by a perceptual hash of the image.

Plan limits never fail

Metered calls are Worker searches, reaction suggestions and photo classifications. Periods are monthly, in UTC, with no daily caps. On-device searches, packs and shards are never metered.

PlanWorker calls a monthPhoto to emoji a month
Free100k100
Solo500k1k
Pro3M10k
Scale15M75k

Over a limit, the API does not return an error:

  • A query that is in the shared edge cache is still answered, with "cached": true, and is not metered.
  • Any other query returns 200 with "overLimit": true: dictionary results only in hybrid mode, no results in semantic mode.
  • The SDK keeps asking, because the edge cache may know the next query. It remembers each over-limit miss and stays on the device and the shards for it.

Privacy

  • Most searches never leave the device.
  • Emojisense never stores IP addresses, keys, user identifiers, message text or images.
  • Search query text that reaches the Worker is cut to 64 characters and stored only in its normalized form. Emojisense’s own jobs use a query only after it was seen at least 5 times.

Read Privacy for exactly what is kept and for how long.