x-reply: Generate contextual, human-sounding replies for X (Twitter) posts with AI.

x-reply: Generate contextual, human-sounding replies for X (Twitter) posts with AI.

PythonChrome ExtensionOpenRouterOpenAIMistral-7B-Instruct
2026-02-26

A Chrome extension that generates contextual, human-sounding replies for X (Twitter) posts using AI.

Links:* GitHub Repository | Follow me on X | My Blog

Introduction

Engaging on X (formerly Twitter) is one of the best ways to network and build an audience. But let's be honest: staring at a blank reply box trying to come up with something witty, thoughtful, or engaging takes time.

I wanted to build a browser extension to integrate Large Language Models (LLMs) directly into my daily browsing workflow, specifically on X. Furthermore, I wanted to learn the intricacies of Chrome's Manifest V3 architecture. The result is Reply X, an open-source Chrome extension that sits right inside the X UI and generates multiple contextual reply options with a single click.

Project Goal

The primary goal of Reply X was to eliminate writer's block while ensuring the AI-generated replies didn't sound like a robotic corporate bot. To achieve this, the extension needed to:

  1. Be Context-Aware: Read not just the target tweet, but also the thread history, quoted tweets, and even attached images.
  2. Sound Human: Allow users to tweak the tone (Casual, Witty, Roasting, Sarcastic) and length on the fly.
  3. Bring Your Own Key (BYOK): Avoid expensive server costs by using OpenRouter, letting users plug in their own API keys and choose their preferred models (from cheap text models to advanced Vision models).
  4. Feel Native: Seamlessly inject UI elements into X's heavily dynamic React-based DOM without breaking performance or looking out of place, including automatic light/dark mode detection.

Project Working

1. Seamless UI Injection (Content Scripts)

X is a Single Page Application (SPA) where tweets load dynamically as you scroll. You can't just run a script once on page load. Instead, I used a MutationObserver in my content script to watch the DOM for new tweets and inject the "Reply X" button seamlessly next to the standard X action buttons.

// content.js
function observeTweets() {
  observer = new MutationObserver((mutations) => {
    if (pendingInjection) {
      cancelAnimationFrame(pendingInjection);
    }
    
    // Batch DOM updates using requestAnimationFrame for performance
    pendingInjection = requestAnimationFrame(() => {
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE) {
            injectButtonsToTweets(node);
          }
        }
      }
      pendingInjection = null;
    });
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
}

2. Deep Context Extraction

Generating a good reply requires context. When you click the Reply X button, the script parses the DOM to extract the author's username, the tweet text, quoted tweets, and previous thread messages.

It even scrapes image URLs from the tweet so that if you select a Vision Model (like Llama 3.2 Vision or Gemini 2.0 Flash Lite), the AI can actually "see" the memes, screenshots, or infographics you are replying to.

3. Serverless AI Communication (Background Service Worker)

Because of Chrome's CORS restrictions and security best practices, the actual API calls happen in the extension's Background Service Worker. I built a robust fetch wrapper with built-in retries and timeouts to handle OpenRouter API requests gracefully.

// background.js
async function fetchWithRetry(url, options, retries = MAX_RETRIES) {
  let lastError;
  
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const response = await fetchWithTimeout(url, options);
      if (response.ok) return response;
      
      const errorData = await response.json().catch(() => ({}));
      
      // Handle rate limits (429) or server errors (500+) with exponential backoff
      if (response.status === 429 || response.status >= 500) {
        if (attempt < retries) {
          const delay = RETRY_DELAY * Math.pow(2, attempt);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
      }
      throw { status: response.status, data: errorData };
    } catch (error) {
      lastError = error;
      if (attempt < retries && !error.message?.includes('timed out')) {
        const delay = RETRY_DELAY * Math.pow(2, attempt);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
  throw lastError;
}

4. Smart Prompt Engineering & Parsing

To get exactly 5 distinct options, I crafted a strict system prompt. The background script then parses the AI's markdown/numbered list output using Regex to cleanly separate the suggestions, calculate character limits (highlighting if it exceeds X's 280 limit), and present them in the custom popup UI.

Key Takeaways

  1. Manifest V3 Architecture: Building this taught me the strict separation of concerns in modern Chrome extensions. Content scripts handle the DOM, the Settings page handles local storage, and the Background Service Worker handles network requests and message passing.
  2. DOM Manipulation in React SPAs: Injecting elements into an obfuscated, highly dynamic React tree (like X's) is tricky. Using MutationObserver combined with requestAnimationFrame ensured the extension didn't cause performance lag or memory leaks while scrolling endlessly.
  3. The Power of Vision LLMs: Adding image URL extraction and passing them to OpenRouter's vision models completely changed the quality of the outputs. Replying to a meme with context-aware text feels like magic compared to text-only models that have no idea what the picture is.

Conclusion

Building Reply X was an incredibly fun dive into browser extensions and applied AI. It successfully solves the "blank canvas" problem on social media, letting me engage faster while retaining my preferred tone.

Because it relies on OpenRouter, the extension is completely free of subscription fees—you just pay fractions of a cent per API call directly to the model providers.

If you want to speed up your X engagement or just want to see how to build a production-ready Manifest V3 extension, grab the code and try it out!

# Clone the repo and load it unpacked in chrome://extensions/
git clone https://github.com/xyzprtk/reply-x.git

The code is fully open source under the MIT License. I'd love to see what features you might add.

Happy tweeting!