DEV Community

Cover image for You Click "Add to Story" and Instagram Does Way More Than You Think
Ankit Rattan
Ankit Rattan

Posted on

You Click "Add to Story" and Instagram Does Way More Than You Think

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>
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)