Skip to content

Repository files navigation

LegacyChat Logo

LegacyChat

A zero-dependency, single-file AI chatbot crafted for vintage WebKit browsers and legacy hardware.

Tested and optimized for iPad Mini 1st Gen (iOS 9.3.6), older mobile Safari, and retro web engines.

JavaScript ES5 HTML5 CSS3 OpenRouter MIT License Vercel Ready

OverviewThe Engineering ChallengeKey FeaturesQuick StartArchitectureScreenshotsTroubleshooting & FAQDocumentation HubLicense


LegacyChat Interface on iPad Mini

Overview

Modern web applications increasingly demand cutting-edge JavaScript features (fetch(), async/await, ES6 modules, CSS Grid, and custom properties) that fail completely on pre-2017 mobile browsers. Consequently, millions of capable vintage tablets—such as the iPad Mini 1st Generation (Apple A5, iOS 9.3.6)—are excluded from experiencing modern Large Language Model interfaces.

LegacyChat solves this problem entirely.

Contained within a single standalone file (index.html), LegacyChat delivers a state-of-the-art AI chat experience with glassmorphism aesthetics, live token-by-token streaming, Markdown parsing, multimodal camera attachments, and exportable chat history—without requiring Node.js, Webpack, Babel, npm dependencies, or intermediate proxy servers.

Important

Single-File Zero-Dependency Guarantee
Everything needed to run LegacyChat lives inside index.html. Open the file locally in any web browser or drop it onto any static web host to begin immediately.


The Engineering Challenge

Safari on iOS 9.3.6 runs a 2016-era WebKit engine with hard architectural limits: dual-core 1.0 GHz Apple A5 processor, 512MB RAM, no fetch() API, no Promises, and no ES6 grammar.

LegacyChat bridges every modern web API to strict legacy-compatible standards:

Modern Web Expectation Safari 9.3.6 Status LegacyChat ES5 Solution
fetch() & ReadableStream Unsupported Native XMLHttpRequest polling buffer on readyState === 3
const, let, () => {} Unsupported Strict ES5 var, explicit function closures, prototype methods
async / await & Promises Unsupported Asynchronous callback chains with discrete error propagation
CSS Grid Unsupported Flexible -webkit-box and -webkit-flex box layout fallback systems
CSS gap property Unsupported Adjacent sibling combinators (.item + .item { margin-left: X })
CSS Custom Variables (var(--)) Unsupported Dynamic DOM stylesheet rules and explicit theme body classes
Modern Event Constructors (new Event()) Unsupported Backward-compatible document.createEvent('Event') dispatching
Element.prototype.remove() Unsupported Traditional node.parentNode.removeChild(node) DOM removal
High-Frequency DOM Repaints Layout Thrashing 60ms throttled streaming renderer (~16fps) conserving A5 CPU cycles
5MB localStorage Limits Quota Crashes Client-side Canvas JPEG downsampling and SVG image placeholder caching

Key Features

Pure ES5 Execution

  • Zero ES6 syntax: verified using strict ECMAScript 5 abstract syntax tree validation.
  • Compatible with Safari 9.3.6, iOS 8+, older Android WebViews, and desktop legacy browsers.
  • Requires zero transpilation steps or build systems.

60ms Low-Power Streaming Buffer

  • Streams token deltas from OpenRouter Server-Sent Events (SSE).
  • Throttles DOM markdown updates to 60ms intervals (~16fps), eliminating UI freeze and battery drain on low-spec mobile chips.
  • Native autoscroll engine tracks streaming output without layout stutter.

Built-in Markdown Engine

  • Full parser for headers, bold, italics, inline code, and strikethrough text.
  • Fenced code blocks with language headers and one-tap Copy Code functionality.
  • GitHub Flavored Markdown (GFM) table support with alternating row formatting.
  • Ordered and unordered nested list generation.

Multimodal Vision & Image Downsampling

  • Attach photos directly from device camera roll or file inputs.
  • Automatic off-screen <canvas> image downsampler scales photos to a maximum boundary of 800x800 pixels at 0.6 JPEG quality.
  • Reduces multi-megabyte camera files to lightweight Base64 payloads (~50KB) to prevent browser memory exhaustion.

Comprehensive Session & Quota Management

  • Full export and import of chat histories in structured JSON format.
  • Edit prior prompts with automatic response regeneration.
  • Delete individual messages or clear entire conversation threads.
  • Automatic startup routine prunes orphaned draft keys to preserve the 5MB browser storage quota.
  • Preserves storage quota by converting older chat images to compact SVG placeholders.

Dynamic OpenRouter Model Catalog

  • Connects directly to the public OpenRouter API to fetch and sort available AI models.
  • Includes presets for top industry models (Claude Sonnet 4.5, GPT-4o, Gemini 3.5 Flash, DeepSeek V4.1 Flash).
  • Features verified 100% free models by default (Google Gemma 4 31B Free, Gemma 4 26B Free, Qwen 3.8 27B Free, Cohere North Mini Code Free).
  • Customizable system persona templates (Coding Assistant, Translator, Academic, Creative, or Custom).

Quick Start

Option 1: Open Directly in Your Browser (No Setup Required)

Clone the repository and open index.html in your browser:

git clone https://github.com/gininaba/legacychat.git
cd legacychat
open index.html

Option 2: Run with a Local Static Server

If testing across devices on your local network (e.g., streaming over Wi-Fi to your iPad Mini):

# Using Python 3
python3 -m http.server 8080

# Using Node.js (npx)
npx serve .

# Using PHP
php -S 0.0.0.0:8080

Access the server on your iPad Mini by entering your computer's local IP address (e.g., http://192.168.1.50:8080).

Option 3: Deploy to Vercel (or Any Static Host)

  1. Push this repository to your GitHub account.
  2. Log in to Vercel and click Add New Project.
  3. Select your legacychat repository and click Deploy.
  4. Your application will be live at https://your-project.vercel.app with full HTTPS support.

Option 4: Add to iPad Home Screen (Standalone App Mode)

  1. Open your deployed URL in Safari on your iPad Mini.
  2. Tap the Share button in Safari's toolbar.
  3. Tap Add to Home Screen.
  4. Launch LegacyChat from the home screen icon to enjoy a distraction-free, full-screen app view without browser address bars.

Architecture

LegacyChat follows a modular, single-file architecture. All communication and data transforms happen client-side in the browser:

flowchart TD
    subgraph Client [Browser Runtime: index.html]
        UI[User Interface & Inputs]
        Canvas[Hidden Canvas Resizer]
        State[Global ES5 State Store]
        Parser[formatMarkdown & sanitizeHTML]
        Storage[(Local Storage Quota Guard)]
        Timer[60ms Throttle Interval]
    end

    subgraph Remote [OpenRouter Cloud]
        OR_Models[GET /api/v1/models]
        OR_Chat[POST /api/v1/chat/completions]
    end

    UI -->|Photo Upload| Canvas
    Canvas -->|Base64 JPEG <= 800px| State
    UI -->|Submit Message| State
    State -->|XMLHttpRequest SSE Streaming| OR_Chat
    OR_Chat -->|Raw Chunk Stream| State
    State -->|Accumulated Deltas| Timer
    Timer -->|Render Event| Parser
    Parser -->|Safe HTML Injection| UI
    State -->|Debounced Serialization| Storage
    UI -->|Refresh Models Button| OR_Models
    OR_Models -->|Populate Dropdown| State
Loading

Configuration & Settings

LegacyChat communicates directly with OpenRouter. All configuration options are saved locally on your device in localStorage:

Setting Storage Key Description Default
API Key legacychat_api_key OpenRouter authorization token (sk-or-v1-...) Empty
Model legacychat_model Selected AI model identifier slug google/gemma-4-31b-it:free
Persona legacychat_persona_select Active system persona prompt preset General Assistant
Custom Persona legacychat_custom_persona User-defined system instructions Empty
Temperature legacychat_temp Output randomness (0.0 deterministic to 2.0 creative) 0.7
Max Tokens legacychat_tokens Maximum generation token limit per response 2048
Theme legacychat_theme Visual appearance (dark or light) dark

Screenshots

Primary Chat View Setup & Configuration
Primary Chat Interface Setup Screen
Chat History Drawer Dynamic Model Catalog
Chat History Drawer Models Selection
System Persona Selection Clean Light Mode
Persona Modal Light Theme

Troubleshooting & FAQ

1. Error: "This request requires more credits, or fewer max_tokens..."

This occurs when your OpenRouter account balance or token credit limit is lower than the configured Max Tokens limit (2048 tokens).

Resolution:

  • Open Settings in LegacyChat.
  • Under the Advanced section, adjust Max Tokens to a lower value (e.g., 80 or 150).
  • Alternatively, select one of the free models from the model dropdown (e.g., google/gemma-4-31b-it:free or qwen/qwen3.8-27b:free).
  • Or add credits to your balance in your OpenRouter Dashboard.
2. Why does my API key disappear after closing Safari?

If Safari is running in Private Browsing Mode, WebKit isolates all localStorage writes in temporary volatile memory and clears them when tabs are closed.

Resolution:

  • Exit Private Browsing mode in Safari to allow persistent storage of your preferences and API key.
3. What should I do if I see a "Storage Limit Exceeded" alert?

Safari enforces a strict 5MB quota on local browser storage. Storing many extended conversations with high-resolution image uploads can approach this limit.

Resolution:

  • Open the History Drawer (top-left menu button).
  • Click Export to save an offline JSON backup of all your conversations.
  • Click Clear All to purge old stored threads and free device memory.
4. Does LegacyChat transmit my API key through any intermediary server?

No. LegacyChat has zero backend code. Every network request is initiated directly from your device browser to https://openrouter.ai/api/v1/chat/completions. Your API key never leaves your local device storage except in standard HTTPS headers sent directly to OpenRouter.


Documentation Hub

Comprehensive engineering guides are available for contributors and developers:

  • Developer Onboarding Guide: Environment setup, ES5 verification commands, and USB remote debugging with Safari Web Inspector.
  • Component Architecture: Complete index of DOM element structures, CSS class tokens, and JavaScript helper functions.
  • API & Streaming Manual: Detailed explanation of OpenRouter payloads, base64 vision serialization, and SSE buffer parsing.
  • Contribution Guidelines: Coding standards, ES5 testing checklist, and pull request workflows.
  • Changelog: Chronological history of releases, optimizations, and bug fixes.

Project Structure

legacychat/
├── index.html        # Unified single-file web app (HTML markup, CSS design system, and ES5 engine)
├── logo.png          # High-resolution application brand logo (1024x1024)
├── apple-touch-icon.png # Optimized iOS home screen icon (180x180)
├── LICENSE           # Standard MIT License terms
├── README.md         # Master project documentation
├── CHANGELOG.md      # Detailed version releases and patch notes
├── COMPONENTS.md     # DOM elements, state variables, and function references
├── API.md            # OpenRouter endpoints, payload models, and SSE streaming specifications
├── ONBOARDING.md     # Contributor quickstart, ES5 syntax enforcement, and hardware test guidelines
├── CONTRIBUTING.md   # Guidelines for issues, code reviews, and pull requests
└── Screenshots/      # Visual application previews
    ├── Chat.png
    ├── SetupScreen.png
    ├── ChatHistory.png
    ├── Models.png
    ├── Persona.png
    └── LightTheme.png

License

This project is licensed under the MIT License. See LICENSE for the full license text.

Copyright (c) 2026 gininaba

About

A zero-dependency, single-file AI chatbot crafted for vintage WebKit browsers and legacy hardware.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages