A while back I noticed something in a TikTok comment thread that didn't make sense to me. People were typing what looked like emojis I'd never seen in any standard keyboard. A shaking face. A specific cartoon thumbs-up that didn't match the iOS or Android version. A speechless head-tilt.
At first I assumed they were stickers, or some creator-only feature. But the more I scrolled, the more I noticed regular accounts using them in plain text comments. They were typing something. The platform was rendering it as a custom emoji.
What I found over the next two weeks turned into TTEmos — a reference site for TikTok's 46 undocumented hidden emoji codes. This is the technical version of how that worked.
The first lead
The first thing I did was right-click "Inspect" on a comment that contained one of these mystery emojis. The DOM showed an <img> tag pointing to an asset on TikTok's CDN. Nothing unusual there, except that the user had clearly typed plain text into the comment box — the input field had no emoji picker open, and there was no sticker UI involved.
That meant somewhere between the user typing and the comment rendering, TikTok was doing a text replacement.
I went to the DevTools Network tab, posted a test comment with the text [smile], and watched. Two things happened:
- The submit request sent the literal string
[smile]to the API, with no client-side replacement. - When the comment came back from the server and rendered, the text was gone and an image was in its place.
So the replacement was happening on render, against a known list of codes the platform recognized.
Finding the list
The next step was figuring out where this known list lived. The likely candidates were:
- A JS bundle on the web client that did the replacement after fetch
- Server-rendered HTML that arrived with the image already in place
- A separate config endpoint the client fetched on load
I spent about an hour going through bundled JS files in the Sources tab, looking for anything that mapped a square-bracket-wrapped string to an image filename. After narrowing it down, I found a chunk of code with the structure I was looking for. Roughly (paraphrased — not the literal source):
const EMOJI_MAP = {
"[smile]": "/emoji/smile.png",
"[happy]": "/emoji/happy.png",
"[shock]": "/emoji/shock.png",
// ...
};
function renderCommentText(text) {
return text.replace(/\[\w+\]/g, (match) => {
const asset = EMOJI_MAP[match];
return asset ? `<img src="${asset}">` : match;
});
}
Once I had the map, I had the codes. There were 46 of them.
Validation
Having a list extracted from a JS file is a hypothesis, not a confirmed reference. The list could include codes that no longer worked. It could exclude codes that lived in a different code path. I needed to validate every entry on the live platform.
So I made a test account and started typing each code into comments, one at a time. For each code I checked:
- Does the code render as an emoji at all?
- Is the rendered emoji visually distinct from the others?
- Does it render the same way in different contexts (comment, caption, reply)?
- Does it survive the
Editflow without breaking?
Most codes passed cleanly. A few were interesting:
- Three codes were effectively deprecated — they were in the JS map but rendered as literal text on the live platform.
- Two codes had visually identical outputs (they pointed to the same asset).
- Some codes had region-specific rendering — they worked normally in some locales but not others.
I documented each of these on the reference site as separate metadata fields.
The reference site itself
Once I had the validated list, the question became how to make it useful.
The temptation with this kind of niche knowledge is to write a long blog post listing everything. That's how most fragments of this list had been published before — buried in Reddit threads, halfway through YouTube tutorials, scattered across screenshots in TikTok comments themselves. The problem is that a blog post is something you read once. People who actually wanted to use these codes needed something they'd come back to dozens of times.
So I built a static site. No backend. No database. One page with all 46 emojis, a search/filter, and a one-click copy button on each one.
The architecture was deliberately boring:
ttemos.com/
├── index.html # main reference page
├── emojis.json # the validated 46 entries
└── assets/
└── *.png # locally hosted emoji images
A small bit of JavaScript handles search, filter, and copy-to-clipboard:
async function copyCode(code, button) {
try {
await navigator.clipboard.writeText(code);
button.classList.add('copied');
setTimeout(() => button.classList.remove('copied'), 1500);
} catch (err) {
// fall back to a hidden textarea + execCommand for older browsers
fallbackCopy(code);
}
}
That's the whole product. The user opens the site, finds the emoji they want, clicks copy, pastes into a TikTok comment. Three seconds.
I hosted my own copies of the emoji-style icons rather than hot-linking to TikTok's CDN. Hot-linking would have been faster to set up but fragile — TikTok could change their CDN paths at any time and the reference site would break overnight without me even noticing.
A note on undocumented features
There's an ethics question hovering over this kind of project worth addressing directly.
Documenting an undocumented but publicly visible feature isn't reverse-engineering an API in the harmful sense. The codes are publicly visible in the product. Users are typing them in plain text. The codes don't bypass any security boundary, don't expose private data, and don't unlock anything that wasn't already accessible.
What I tried to avoid was anything that would cross a line:
- I didn't rehost TikTok's actual emoji image assets at scale. The reference site uses my own icons in a similar style.
- I didn't build automation that posts to TikTok programmatically.
- I didn't document anything else that came up while looking for the emoji map — internal API endpoints, auth flows, anything in that category stayed in DevTools and out of the article.
The goal was a reference. Nothing more.
What I'd do differently
Two things, looking back:
Build the validation harness first. I validated codes one by one manually, which took hours. If I were starting again, I'd write a small test script that takes a list of candidate codes, posts each one to a throwaway test account, and reports back which render and which don't. The platform makes that easy because the rendered output is in the DOM after submission.
Track the source of each code. Some codes came from the original JS map, some came from community Reddit threads I cross-referenced, and a few I discovered by trying patterns that weren't in either source. I didn't track which was which. When the platform updated their map a few months later and three codes broke, I had no record of which sources had been most reliable historically.
What's there now
TTEmos currently has all 46 validated codes with metadata about regional differences and deprecation status. The full reference is at ttemos.com.
It's a small static site. It will probably never grow into anything bigger. But every time someone wants to type a hidden TikTok emoji, the site is there.
The broader point — for anyone building something similar — is that platforms quietly accumulate undocumented features, and there's real value in being the person who writes them down. Not for the SEO. Not for affiliate clicks. Just because the documentation should exist and nobody else is making it.
What undocumented features have you mapped? Drop a comment.
Top comments (0)