惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
U
Unit 42
博客园 - 三生石上(FineUI控件)
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research
MongoDB | Blog
MongoDB | Blog
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
云风的 BLOG
云风的 BLOG
Jina AI
Jina AI
S
Schneier on Security
T
Threatpost
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
Spread Privacy
Spread Privacy
The Register - Security
The Register - Security
Google DeepMind News
Google DeepMind News
L
LINUX DO - 热门话题
T
Tenable Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
C
CERT Recently Published Vulnerability Notes
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
L
Lohrmann on Cybersecurity
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Hacker News
The Hacker News
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
P
Palo Alto Networks Blog
A
About on SuperTechFans
V
Visual Studio Blog
T
The Exploit Database - CXSecurity.com
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
K
Kaspersky official blog
MyScale Blog
MyScale Blog
月光博客
月光博客
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
腾讯CDC
T
Tailwind CSS Blog
博客园_首页
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 【当耐特】

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
You Click "Add to Story" and Instagram Does Way More Than You Think
Ankit Rattan · 2026-06-19 · via DEV Community

The other day I was randomly scrolling through Instagram and a question popped into my head:

How does Instagram Story upload actually work?

Not from a user's perspective.

From an engineering perspective.

You click a picture, add some text, maybe throw in a song, hit "Your Story", and within a few seconds your followers can start viewing it.

The whole thing feels effortless.

But there has to be a lot happening behind the scenes.

Now obviously, none of us actually know how Instagram's internal architecture works except the engineers at Meta.

And honestly, at Instagram's scale, the real system is probably absolutely crazy.

So I decided to do something fun.

I built a small simulator that visualizes how I think a large-scale social media platform might handle Story uploads.

This is not Instagram's real architecture.

It's simply my attempt to model the different backend responsibilities involved in making a feature like Stories work at scale.

And while building it, I realized how much engineering is hidden behind a button that most of us press every day without thinking.

We All Think Story Uploads Are Simple

For most users, uploading a Story feels like a two-step process.

Take a photo.

Upload it.

Done.

But when you start thinking like an engineer, things become a lot more interesting.

How does the platform know it's really you uploading the Story?

Where does the image actually get stored?

How does it decide who can see it?

How does a Close Friends Story work differently from a normal Story?

And how does all of this happen almost instantly?

Those questions became the foundation of the simulator.


The First Problem Is Trust

Before a platform accepts any Story upload, it needs to verify who is making the request.

Think about it.

If Instagram didn't validate user identity, anyone could potentially upload content pretending to be someone else.

That would be a disaster.

So before the image even enters the system, the platform first needs to establish trust.

It needs to know that the request is coming from an authenticated user.

Users never see this part.

Nobody opens Instagram thinking about authentication tokens or session validation.

But without them, the entire platform falls apart.


Uploading The Image Isn't The Hard Part

Most beginner developers think the difficult part is storing the image.

But modern infrastructure is already very good at storing files.

The more interesting challenge is managing everything around that image.

Once a Story gets uploaded, the platform needs to know:

Who uploaded it?

When was it uploaded?

When should it disappear?

What type of Story is it?

Who should be allowed to view it?

Suddenly the image becomes only a small piece of a much larger system.

The Story isn't just a photo anymore.

It's a collection of rules, metadata, permissions, and lifecycle information.


The Real Challenge Is Deciding Who Gets To See It

This was probably the most interesting part of the simulator.

Because uploading content is relatively straightforward.

Audience calculation isn't.

For a normal Story, the platform has to determine who should actually receive access.

Some users might be blocked.

Some users might be hidden from Stories.

Some privacy settings might apply.

All of these conditions affect the final audience.

And the platform has to perform those calculations extremely fast.

Users expect Stories to appear immediately.

Nobody wants to upload a Story and wait thirty seconds while the backend figures out visibility rules.


Close Friends Is Smarter Than It Looks

The more I thought about Close Friends Stories, the more I appreciated the design.

Because the upload process doesn't really need to change.

The image is still uploaded.

The Story still gets created.

The content is still stored.

Only one thing changes.

The audience.

Instead of evaluating every follower, the system starts with a much smaller list chosen by the creator.

It's such a simple idea from a user's perspective.

But from an engineering perspective, it's a really elegant way of extending an existing feature without rebuilding the entire system.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Instagram Story Upload Simulator (Normal Mode)</title>
    <style>
        :root {
            --ig-primary: #833AB4;
            --ig-secondary: #E1306C;
            --ig-gradient: linear-gradient(45deg, #FFDC80, #FCAF45, #F77737, #E1306C, #C13584, #833AB4, #5B51D8, #405DE6);
            --service-bg: #f4f4f9;
            --border-color: #ddd;
            --text-main: #333;
            --text-sub: #666;
            --packet-color: cyan;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background-color: #fafafa;
            color: var(--text-main);
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        /* --- Global Structure --- */
        #simulator-container {
            width: 100%;
            max-width: 1200px;
            background: #fff;
            border: 1px solid var(--border-color);
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0,0,0,0.05);
        }

        header {
            background-color: var(--service-bg);
            padding: 20px;
            border-bottom: 1px solid var(--border-color);
            text-align: center;
        }

        header h1 {
            margin: 0;
            font-size: 1.5rem;
            background: var(--ig-gradient);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        header p {
            margin: 5px 0 0;
            color: var(--text-sub);
            font-size: 0.9rem;
        }

        /* --- Controls Section --- */
        #controls-panel {
            display: flex;
            justify-content: space-between;
            padding: 20px;
            background: #fff;
            border-bottom: 1px solid var(--border-color);
            gap: 20px;
        }

        .control-group {
            flex: 1;
            padding: 15px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            background-color: var(--service-bg);
        }

        .control-group h3 {
            margin: 0 0 10px;
            font-size: 1rem;
            color: var(--ig-secondary);
        }

        select, button {
            width: 100%;
            padding: 10px;
            border-radius: 6px;
            border: 1px solid #ccc;
            font-size: 1rem;
            margin-top: 5px;
        }

        select {
            background-color: #fff;
        }

        button {
            cursor: pointer;
            transition: background-color 0.2s;
        }

        #story_type_select:disabled {
            background-color: #f0f0f0;
            color: #999;
            cursor: not-allowed;
        }

        #simulate-btn {
            background: var(--ig-gradient);
            color: white;
            border: none;
            font-weight: bold;
            font-size: 1.1rem;
            margin-top: 20px;
        }

        #simulate-btn:hover {
            opacity: 0.9;
        }

        #simulate-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }

        /* --- Workflow Diagram --- */
        #workflow-diagram {
            position: relative;
            padding: 40px 20px;
            height: 250px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .service-node {
            position: absolute;
            width: 120px;
            height: 90px;
            background: #fff;
            border: 2px solid var(--border-color);
            border-radius: 10px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            font-size: 0.8rem;
            text-align: center;
            transition: transform 0.3s, border-color 0.3s, box-shadow 0.3s;
            box-sizing: border-box;
            z-index: 2;
        }

        .service-node.active {
            border-color: var(--ig-primary);
            transform: scale(1.05);
            box-shadow: 0 0 15px rgba(131, 58, 180, 0.3);
        }

        .service-node .icon {
            font-size: 2rem;
            margin-bottom: 5px;
        }

        .service-node .label {
            font-weight: bold;
            color: var(--text-main);
        }

        /* Pre-positioning nodes for "normal story" flow */
        #node-app { left: 5%; top: 50%; transform: translateY(-50%); }
        #node-api { left: 25%; top: 50%; transform: translateY(-50%); }
        #node-media { left: 45%; top: 50%; transform: translateY(-50%); }
        #node-story { left: 65%; top: 50%; transform: translateY(-50%); }
        #node-audience { left: 85%; top: 50%; transform: translateY(-50%); }

        /* Flow lines */
        .flow-line {
            position: absolute;
            height: 2px;
            background-color: var(--border-color);
            z-index: 1;
        }

        /* Define specific lines */
        #line-app-api { left: calc(5% + 120px); top: 50%; width: calc(20% - 120px); }
        #line-api-media { left: calc(25% + 120px); top: 50%; width: calc(20% - 120px); }
        #line-media-story { left: calc(45% + 120px); top: 50%; width: calc(20% - 120px); }
        #line-story-audience { left: calc(65% + 120px); top: 50%; width: calc(20% - 120px); }

        /* Data Packet */
        #data-packet {
            position: absolute;
            width: 16px;
            height: 16px;
            background-color: var(--packet-color);
            border-radius: 50%;
            display: none;
            z-index: 3;
            box-shadow: 0 0 8px var(--packet-color);
            transform: translate(-50%, -50%);
        }

        /* Description Box */
        #description-panel {
            padding: 15px 20px;
            background-color: var(--service-bg);
            border-top: 1px solid var(--border-color);
            color: var(--text-sub);
            font-size: 0.95rem;
            min-height: 40px;
            text-align: center;
        }

        /* --- Audience Visualization Section --- */
        #audience-panel {
            padding: 30px;
            background: #fff;
            border-top: 1px solid var(--border-color);
        }

        #audience-panel h2 {
            margin: 0 0 20px;
            font-size: 1.3rem;
            text-align: center;
            color: var(--ig-secondary);
        }

        #logic-summary {
            background-color: var(--service-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 30px;
            font-family: monospace;
            text-align: center;
            color: var(--text-main);
        }

        #logic-summary .rule { color: var(--ig-primary); font-weight: bold;}
        #logic-summary .calculation { display: none; }

        /* Follower Grid */
        #follower-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }

        .follower-avatar {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
            opacity: 0.3; /* Greyed out initially */
            transition: opacity 0.5s, transform 0.5s;
        }

        .avatar-circle {
            width: 80px;
            height: 80px;
            border-radius: 50%;
            background: #eee;
            border: 2px solid #ccc;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 2rem;
            margin-bottom: 10px;
            position: relative;
        }

        .avatar-name {
            font-size: 0.85rem;
            color: var(--text-sub);
        }

        /* Highlight states */
        .follower-avatar.eligible {
            opacity: 1;
        }
        .follower-avatar.eligible .avatar-circle {
            background: var(--ig-gradient);
            border-color: #fff;
            box-shadow: 0 4px 10px rgba(225, 48, 108, 0.3);
        }
        .follower-avatar.eligible .avatar-name {
            color: var(--ig-primary);
            font-weight: bold;
        }

        /* Blocked state */
        .follower-avatar.blocked {
            opacity: 1;
            filter: grayscale(100%);
        }
        .follower-avatar.blocked .avatar-circle::after {
            content: '✕';
            position: absolute;
            font-size: 3rem;
            color: #ff4444;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-weight: bold;
        }

        /* Viewer Access Summary Box */
        #access-summary {
            background: #fff;
            border: 2px solid var(--border-color);
            border-radius: 10px;
            padding: 20px;
            text-align: center;
            transition: border-color 0.5s;
        }

        #access-summary.final {
            border-color: var(--ig-secondary);
        }

        #access-summary h4 {
            margin: 0 0 10px;
            color: var(--text-sub);
        }

        #viewer-list {
            font-size: 1.1rem;
            font-weight: bold;
            color: var(--text-main);
        }

        /* Keyframe Animations */
        @keyframes movePacket {
            0% { left: 0%; top: 0%; opacity: 0; }
            10% { opacity: 1; }
            90% { opacity: 1; }
            100% { left: 100%; top: 0%; opacity: 0; }
        }

    </style>
</head>
<body>

    <div id="simulator-container">
        <header>
            <h1>Instagram Story Upload Simulator</h1>
            <p>End-to-end backend workflow visualization (Mode: Normal)</p>
        </header>

        <!-- Initialize State (Hardcoded for simulation) -->
        <script>
            const CREATOR = { id: 123, name: "Ankit" };
            const FOLLOWERS = [
                { id: "A", name: "Alice", avatar: "👩🏼" },
                { id: "B", name: "Bob", avatar: "👨🏾" },
                { id: "C", name: "Charlie", avatar: "👨🏻" },
                { id: "D", name: "David", avatar: "👨🏼" },
                { id: "E", name: "Eve", avatar: "👩🏾" },
                { id: "F", name: "Frank", avatar: "👨🏿" }
            ];
            const BLOCKED_BY_CREATOR = ["D"]; // Sample 'David' is blocked.
            // Simplified: We don't need hidden lists for this simple demo.
        </script>

        <!-- Controls Section -->
        <section id="controls-panel">
            <div class="control-group">
                <h3>1. Select Story Type</h3>
                <select id="story_type_select" disabled>
                    <option value="normal" selected>Your Story (Normal)</option>
                    <option value="close_friends">Close Friends (Disabled)</option>
                </select>
            </div>

            <div class="control-group">
                <h3>2. Manage Close Friends List</h3>
                <p style="margin: 0; color: #999; font-style: italic; font-size: 0.9rem">Disabled for current simulation.</p>
            </div>

            <div style="flex: 0 0 auto;">
                <button id="simulate-btn" onclick="startSimulation()">Simulate Upload (Normal)</button>
            </div>
        </section>

        <!-- Workflow Diagram -->
        <section id="workflow-diagram">
            <!-- Data Packet -->
            <div id="data-packet"></div>

            <!-- Flow Lines -->
            <div class="flow-line" id="line-app-api"></div>
            <div class="flow-line" id="line-api-media"></div>
            <div class="flow-line" id="line-media-story"></div>
            <div class="flow-line" id="line-story-audience"></div>

            <!-- Nodes -->
            <div class="service-node" id="node-app">
                <div class="icon">📱</div>
                <div class="label">Creator App</div>
            </div>
            <div class="service-node" id="node-api">
                <div class="icon">🔗</div>
                <div class="label">Upload API</div>
            </div>
            <div class="service-node" id="node-media">
                <div class="icon">☁️</div>
                <div class="label">Media<br>Service/CDN</div>
            </div>
            <div class="service-node" id="node-story">
                <div class="icon">🗄️</div>
                <div class="label">Story Service</div>
            </div>
            <div class="service-node" id="node-audience">
                <div class="icon">🧠</div>
                <div class="label">Audience Service</div>
            </div>
        </section>

        <!-- Description Box -->
        <section id="description-panel">
            Click 'Simulate Upload' to start the workflow visualization...
        </section>

        <!-- Audience Visualization Section -->
        <section id="audience-panel">
            <h2>Audience Calculation & Final Viewer Access</h2>

            <div id="logic-summary">
                <span class="rule">Rule (Normal Story):</span><br>
                Eligible Viewers = All Followers - (Blocked + Hidden Users)
                <div class="calculation">
                    <br>Calculation: 6 (Followers) - 1 (Blocked) = <strong style="color:var(--ig-secondary)">5 final viewers.</strong>
                </div>
            </div>

            <div id="follower-grid">
                <!-- Populate avatars dynamically -->
                <script>
                    const grid = document.getElementById('follower-grid');
                    FOLLOWERS.forEach(follower => {
                        const avatar = document.createElement('div');
                        avatar.className = 'follower-avatar';
                        avatar.id = `follower-${follower.id}`;
                        avatar.innerHTML = `
                            <div class="avatar-circle">${follower.avatar}</div>
                            <div class="avatar-name">${follower.name}</div>
                        `;
                        grid.appendChild(avatar);
                    });
                </script>
            </div>

            <div id="access-summary">
                <h4>Final Viewer Access List:</h4>
                <div id="viewer-list">-- Waiting --</div>
            </div>
        </section>

    </div>

    <!-- JavaScript for Simulation -->
    <script>
        const steps = [
            { node: 'node-app', text: `Ankit (UserId: ${CREATOR.id}) prepares a story. Selecting photo, adding text...` },
            { node: 'node-api', text: "API received request. Validating authentication token. Authorizing upload..." },
            { node: 'node-media', text: "Uploading media data. Generating sample CDN storage URL: ig-cdn.com/u123/s987.jpg..." },
            { node: 'node-story', text: "Creating database record. Metadata entry includes field 'story_type': 'normal'..." },
            { node: 'node-audience', text: "Triggering Audience Service logic... Determining eligible followers." },
            { node: 'visualize-logic', text: "Calculating list: All Followers minus (David) due to block list..." },
            { node: 'final-state', text: "Story published! Visualizing device highlights and access summary." }
        ];

        let currentStep = 0;
        const STEP_DURATION = 4000; // 4 seconds per step
        const packet = document.getElementById('data-packet');
        const descPanel = document.getElementById('description-panel');
        const simulateBtn = document.getElementById('simulate-btn');

        function startSimulation() {
            simulateBtn.disabled = true;
            currentStep = 0;
            resetVisualization();
            runNextStep();
        }

        function resetVisualization() {
            // Reset nodes
            document.querySelectorAll('.service-node').forEach(node => node.classList.remove('active'));
            // Reset logic
            document.querySelector('#logic-summary .calculation').style.display = 'none';
            // Reset grid
            document.querySelectorAll('.follower-avatar').forEach(av => av.classList.remove('eligible', 'blocked'));
            // Reset summary
            document.getElementById('access-summary').classList.remove('final');
            document.getElementById('viewer-list').innerText = '-- Waiting --';
            // Hide packet
            packet.style.display = 'none';
            packet.style.animation = 'none';
        }

        function runNextStep() {
            if (currentStep >= steps.length) {
                simulateBtn.disabled = false;
                descPanel.innerHTML = "Simulation complete.";
                return;
            }

            const stepData = steps[currentStep];
            console.log(`Running step ${currentStep}: ${stepData.node}`);

            // Update description
            descPanel.innerText = stepData.text;

            // Highlight the active node (if applicable)
            if (stepData.node.startsWith('node-')) {
                document.getElementById(stepData.node).classList.add('active');
            } else if (currentStep > 0 && steps[currentStep-1].node.startsWith('node-')) {
                 document.getElementById(steps[currentStep-1].node).classList.remove('active');
            }

            // Animate data packet between nodes
            if (currentStep < 4) { // Only moves between the first 5 nodes
                animatePacket();
            } else if (currentStep === 4) {
                 packet.style.display = 'none'; // Reached final destination
            }

            // Specific visualization logic for later steps
            if (stepData.node === 'visualize-logic') {
                 // Unhighlight the Audience Service Brain icon
                document.getElementById('node-audience').classList.remove('active');
                // Show calculation result
                document.querySelector('#logic-summary .calculation').style.display = 'block';
            }

            if (stepData.node === 'final-state') {
                finalizeViewerGrid();
            }

            currentStep++;
            setTimeout(runNextStep, STEP_DURATION);
        }

        function animatePacket() {
            packet.style.display = 'block';
            packet.style.animation = 'none';
            packet.offsetHeight; // trigger reflow

            // Calculate movement between nodes based on index
            const sourceNodeId = steps[currentStep].node;
            const targetNodeId = steps[currentStep + 1].node;

            const startNode = document.getElementById(sourceNodeId);
            const endNode = document.getElementById(targetNodeId);

            // Simplified linear move animation between absolute positioned points
            const startRect = startNode.getBoundingClientRect();
            const endRect = endNode.getBoundingClientRect();
            const diagramRect = document.getElementById('workflow-diagram').getBoundingClientRect();

            const startX = startRect.left + startRect.width / 2 - diagramRect.left;
            const startY = startRect.top + startRect.height / 2 - diagramRect.top;
            const endX = endRect.left + endRect.width / 2 - diagramRect.left;
            const endY = endRect.top + endRect.height / 2 - diagramRect.top;

            // Set initial position
            packet.style.left = startX + 'px';
            packet.style.top = startY + 'px';

            // Define custom keyframe on the fly
            const style = document.getElementById('dynamic-animation') || document.createElement('style');
            style.id = 'dynamic-animation';
            style.innerHTML = `
                @keyframes movePacketDynamic${currentStep} {
                    0% { left: ${startX}px; top: ${startY}px; opacity: 1; }
                    100% { left: ${endX}px; top: ${endY}px; opacity: 1; }
                }
            `;
            document.head.appendChild(style);

            // Apply animation
            packet.style.animation = `movePacketDynamic${currentStep} ${STEP_DURATION}ms linear infinite`;
        }

        function finalizeViewerGrid() {
            // Apply access logic based on initialization data
            let eligibleNames = [];

            FOLLOWERS.forEach((follower, index) => {
                // Add a staggered delay to the avatar animation
                setTimeout(() => {
                    const avatar = document.getElementById(`follower-${follower.id}`);

                    if (BLOCKED_BY_CREATOR.includes(follower.id)) {
                        avatar.classList.add('blocked');
                    } else {
                        avatar.classList.add('eligible');
                        eligibleNames.push(follower.name);
                    }

                    // Update summary at the very end of avatar highlights
                    if (index === FOLLOWERS.length - 1) {
                        setTimeout(() => {
                            document.getElementById('access-summary').classList.add('final');
                            document.getElementById('viewer-list').innerText = eligibleNames.join(', ');
                        }, 500);
                    }

                }, index * 200); // 200ms stagger
            });
        }
    </script>
</body>
</html>

The Biggest Takeaway

Building this simulator wasn't really about Instagram.

It was about understanding how modern software systems are designed.

As developers, we often focus on what users can see.

The button.

The screen.

The image.

The animation.

But the most interesting engineering usually happens behind the scenes.

A feature that feels like a single click often involves authentication, storage systems, metadata management, audience calculations, permissions, caching, and content delivery infrastructure all working together.

Users never notice any of it.

And that's actually the goal.

Great engineering hides complexity.

The smoother the experience feels, the more work is usually happening underneath.

So the next time you upload a Story, remember:

You aren't just posting a photo.

You're triggering an entire chain of backend systems designed to make something incredibly complicated feel incredibly simple.