DEV Community

Cover image for Building ELI-X: A Full-Stack AI App That Translates Complex Papers into Metaphors
Harish Kotra (he/him)
Harish Kotra (he/him)

Posted on

Building ELI-X: A Full-Stack AI App That Translates Complex Papers into Metaphors

As developers, we are constantly bombarded with a massive stream of complex technical papers, 60-page business whitepapers, and long-winded documentation. We don't always have time to read through all the noise. What if we had a single tool that could parse any PDF or webpage and instantly translate it to our level of understanding—whether we want executive bullet points, logic-driven systems, or a simple analogy we could explain to a child?

In this post, we're going to dive into how I built ELI-X (Explain Like I'm 10)—a full-stack web application designed with React 19, Express, Tailwind CSS v4, and the brand-new @google/genai SDK to break down complex PDFs and articles into customized audience profiles across five complexity levels.


Creative Theme & Aesthetic Choice

Rather than building another generic software dashboard, I designed ELI-X using an Artistic Flair / Brutalist Neo-Pop theme. It uses high-contrast ink-like borders (#1A1A1A), thick drop shadows (shadow-[4px_4px_0px_#1A1A1A]), fluid offset interactive movements, and vivid saturated blocks of color.

By utilizing playful fonts and micro-animations, the app's aesthetic reinforces the underlying goal: making technical learning less dry and more approachable.


High-Level Architecture

ELI-X follows a simple, clean full-stack architectural pattern:

  1. Client Interface (React 19): Captures user documents (via drag-and-drop file upload) or web URLs, configures audience personas, sets complexity intensity, and renders responses with elegant markdown.
  2. Server Routing (Express + Multer): Handles API endpoints. For web articles, it fetches raw HTML. For files, it accepts multi-part form payloads and processes PDFs directly in memory.
  3. AI Pipeline (Google Gemini API): Communicates with the official @google/genai SDK to perform multimodal reasoning directly on PDF file buffers and scrape contexts.
[ Client: React 19 ]
       |
       |  (Multipart Form: File Buffer / URL string, Persona, Complexity)
       v
[ Server: Express API ]  --- (Scrapes URL HTML or holds PDF buffer)
       |
       |  (@google/genai SDK)
       v
[ Gemini 3.5 Flash Model ] ---> Returns tailored Markdown explanation
Enter fullscreen mode Exit fullscreen mode

Technical Breakdown: The API Routing Layer

To keep secrets like the GEMINI_API_KEY safe from the client-side browser bundle, all AI calls are proxied through a server-side endpoint.

We set up an Express endpoint /api/explain using multer to handle multipart uploads in memory. Let's look at how we parse the input and formulate our Gemini payload:

import express from 'express';
import multer from 'multer';
import { GoogleGenAI } from '@google/genai';

const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

app.post('/api/explain', upload.single('file'), async (req, res) => {
  try {
    const { persona, type, url, intensity = 3 } = req.body;
    const file = req.file;

    let contents: any[] = [];
    const prompt = `Explain the following content clearly and accurately, tailored exactly to this persona: ${persona}. 
    The desired intensity/complexity level within this persona is ${intensity} out of 5 (where 1 is the simplest, 3 is standard, and 5 is detailed). Adjust your depth and vocabulary accordingly.
    Keep the tone appropriate for the persona (e.g., simple for a 10-year-old, professional for a CEO, technical for a Software Engineer, warm for Grandma).
    Make it engaging, informative, and formatted with markdown. Do not include introductory fluff, just dive into the explanation.`;

    contents.push({ text: prompt });

    if (type === 'file') {
      if (!file) return res.status(400).json({ error: 'File is required' });

      // Convert raw buffer to Base64 to send to Gemini
      const base64Data = file.buffer.toString('base64');
      contents.push({
        inlineData: {
          mimeType: file.mimetype,
          data: base64Data
        }
      });
    } else if (type === 'url') {
      if (!url) return res.status(400).json({ error: 'URL is required' });

      // Fetch webpage content
      const response = await fetch(url);
      const html = await response.text();
      contents.push({ text: `Source URL: ${url}\n\nWebpage content:\n${html}` });
    }

    // Call Gemini 3.5 Flash - optimized for speed and large multimodal contexts
    const response = await ai.models.generateContent({
      model: 'gemini-3.5-flash',
      contents: { parts: contents },
    });

    res.json({ explanation: response.text });
  } catch (error) {
    res.status(500).json({ error: 'Internal Server Error' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Creative Styling with Tailwind CSS v4 and Motion

Tailwind CSS v4 introduces unified native compilation, allowing us to build custom themes extremely quickly.

For the persona buttons, we map metadata-driven layouts directly:

const PERSONAS = [
  { id: 'Age 10', label: '10-Year-Old', short: '10', color: 'bg-[#FFD700]', sub: 'Simple & Fun' },
  { id: 'College student', label: 'College Student', short: 'CL', color: 'bg-[#98FF98]', sub: 'Structured & Academic' },
  { id: 'CEO', label: 'CEO', short: 'CEO', color: 'bg-[#87CEEB]', sub: 'Bullet Points & ROI' },
  { id: 'Software engineer', label: 'Software Engineer', short: 'SE', color: 'bg-[#E6E6FA]', sub: 'Logic & Systems' },
  { id: 'Grandma', label: 'Grandma', short: 'GM', color: 'bg-[#FFB6C1]', sub: 'Warm & Conversational' },
];
Enter fullscreen mode Exit fullscreen mode

By dynamic rendering, clicking any persona instantly modifies the state, rendering custom themes on the fly.

To make user interactions feel delightful and tactile, we use Motion (f.k.a. Framer Motion) to animate state transitions smoothly:

<AnimatePresence mode="wait">
  {inputType === 'pdf' ? (
    <motion.div 
      key="pdf"
      initial={{ opacity: 0, y: 5 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: -5 }}
      transition={{ duration: 0.15 }}
    >
       {/* File upload drag/drop zone */}
    </motion.div>
  ) : (
    // URL input zone
  )}
</AnimatePresence>
Enter fullscreen mode Exit fullscreen mode

Key Takeaways & Developer Benefits

  • Native PDF ingestion: Rather than compiling complex client-side PDF.js decoders, we let Gemini's native multimodal engine ingest the raw PDF stream. This dramatically reduces client-side bundle size and guarantees highly accurate contextual interpretations.
  • Micro-Copywriting: Fine-tuned prompt guidelines combined with direct user-controlled dials (Complexity sliders from 1 to 5) offer unmatched control over reading density.
  • Local Cache Tab: Simple Local Session array states keep recent answers alive across clicks, preventing unnecessary double calls to the server.

Try It Out!

Check out ELI-X now to simplify your readings, built with passion and precision: https://www.dailybuild.xyz/project/192-eli-x

Top comments (0)