Direct messages contain our most intimate conversations, jokes, memories, and photos. Yet, when searching for tools to visualize chat history or generate Spotify Wrapped-style stats for group chats, virtually every existing tool asks for your Instagram login or forces you to upload your sensitive ZIP archives to their cloud servers.
I wanted to change that. I built Stats Insta — a completely free, 100% client-side Instagram Chat Analyzer where your data never leaves your device.
In this article, I will share the engineering challenges behind parsing multi-megabyte JSON exports in the browser, decoding escaped Unicode emojis, computing deep conversational metrics, and generating 9:16 Instagram Story cards directly from the DOM.
🔒 The Architecture: 100% Client-Side Privacy
The core philosophy behind Stats Insta is Local-First Processing:
- Zero Server Uploads: The application does not have a backend parsing server or database for chat messages.
-
Native HTML5 FileReader: When a user drops a
message_1.jsonfile, the browser reads the file directly into client-side RAM usingFileReader.readAsText(). - Session-Only Memory: Once you refresh or close the browser tab, the data is instantly garbage-collected by the browser engine.
You can verify this in Chrome DevTools (F12 > Network Tab): dropping a 50MB conversation file triggers 0 network POST requests.
⚙️ Technical Challenge 1: Decoding Instagram's Unicode Escapes
When Meta exports your message history in JSON format, emojis and special characters are not stored as standard UTF-8 strings. Instead, they are encoded as escaped byte sequences (e.g., \u00e2\u009d\u00a4 for a red heart).
If rendered directly in React, emojis appear as garbled characters (Mojibake). To resolve this, I implemented an in-memory Unicode normalization pipeline:
const decodeUnicode = (unicodeString: string): string => {
try {
return decodeURIComponent(
unicodeString.replace(/\\u([0-9a-fA-F]{4})/g, "%u$1")
);
} catch (error) {
return unicodeString;
}
};
This converts raw escaped sequences into native browser-rendered emojis at runtime without lagging the main thread.
📊 Technical Challenge 2: Calculating Deep Conversational Metrics
Simple message counts are boring. People care about the hidden dynamics of their friendships. We compute several unique behavioral metrics in browser memory:
1. The "Night Owls" Tracker (12:00 AM – 4:59 AM)
We parse the Unix millisecond timestamps (timestamp_ms) on each message and isolate activity occurring in the late-night window:
const hour = new Date(msg.timestamp_ms).getHours();
if (hour >= 0 && hour <= 4) {
nightOwlCounts[msg.sender_name] = (nightOwlCounts[msg.sender_name] || 0) + 1;
}
This immediately reveals who stays awake sending reels and memes while everyone else is asleep.
2. The Conversation Starter Algorithm
Who puts in the effort to keep the conversation alive? We sort the timeline chronologically and scan for silence gaps exceeding 12 hours:
const TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
if (lastTimestamp === 0 || msg.timestamp_ms - lastTimestamp > TWELVE_HOURS_MS) {
starterCounts[msg.sender_name] = (starterCounts[msg.sender_name] || 0) + 1;
}
lastTimestamp = msg.timestamp_ms;
3. The Swear Jar
We scan message strings against a local tokenized dictionary of 100+ global curse words and slang to generate a friendly group chat leaderboard of who has the dirtiest mouth.
📱 Technical Challenge 3: Generating 9:16 Instagram Story Wrapped Cards
People love sharing aesthetic visual summaries with their friends. To make the tool virally shareable, I built an in-browser Story Card generator that renders a 1080x1920 (9:16 Full HD) graphic using HTML5 Canvas:
- Glassmorphism gradient backgrounds with radial glowing orbs
- Rounded progress bars showing conversational dominance percentages
- Dynamic badges and highlights
-
1-Click Native Share: On mobile devices, we invoke the Web Share API (
navigator.share({ files: [pngFile] })), which opens the native iOS/Android share sheet directly into Instagram Stories!
🚀 Try It Live & Explore the Code
The project is completely free to use, and I have published detailed guides on understanding Meta's export architecture:
- 🌐 Live Application: https://stats-insta.vercel.app/
- 📘 Guide: How to Open & Read Instagram message_1.json Files
- 📙 Guide: Activity Heatmaps & Peak Texting Hours
I would love to hear feedback from the DEV community! What other client-side analytics or visual metrics would you like to see added?
Top comments (1)
The decoder snippet turns each escape into
%uXXXX, butdecodeURIComponentdoes not decode that format, so it should hit the catch and return the original text. Did the working parser receive a different input shape from Meta's export?