How I built an AI chat that needs no JavaScript, and why
Why
I read on a Kindle, sometimes in a language that isn't my own. The Kindle can translate a word, but often I want more than that: what an expression really means, who someone is, or why a scene matters. That's a job for an AI chat. But ChatGPT and the other chat sites are far too heavy for the Kindle's browser. They don't display properly, and in practice they're unusable.
At first I assumed I'd need a little JavaScript. Then I thought it would be more fun to make "no JavaScript at all" the rule and see how far that goes. Part of the push came from justfuckingusehtml.com.
It isn't rocket science, just a fun weekend project. It also turns out to be useful beyond the Kindle. It works in restricted browsers, and it suits people who turn JavaScript off for privacy. A page made of plain text and a form may also work better with a screen reader than most JavaScript-heavy chat apps, though I haven't tested that properly.
Small and fast
The homepage and this page are static files served from a CDN. A first visit downloads under 5 KB in all, including the stylesheet and icon, and no JavaScript, so there's nothing to download, parse or run before you can type. Only the chat itself runs on a server, and it starts sending the page before the model has written a word. On a slow connection, you see your question straight away and the answer as it's written.
Streaming without JavaScript
Browsers have always shown a page while it's still downloading, and that's all streaming needs. When you press Send, the server sends the top of the page right away, with your conversation so far and your new question. It keeps the connection open and adds the answer as the model writes it. At the end it adds a fresh form for your next question and closes the page.
The catch is that you can only add to the page. Nothing already sent can be changed or removed, which rules out the usual loading spinner. Instead, each answer starts with a "Thinking…" line, and the server sends a one-line style rule just before the first words of the answer to hide it. The security policy allows that exact rule and no other inline style. On e-ink screens the line stays still; elsewhere its dots step along.
A few small tricks help:
- The form sends you to
#latest-message, and the server puts that anchor just above your newest question, so the browser scrolls there by itself. - The server sends about 2 KB of padding before the answer, because some browsers wait for a chunk of data before showing anything. It helps in some browsers; Safari seems to ignore it.
- If you leave mid-answer, the server notices and cancels the request to the model, so nobody pays for an answer nobody reads.
Markdown, in pieces
Models answer in Markdown, and the answer arrives in small chunks that ignore the formatting. Bold text can arrive as **bo and then ld**. A line starting with # could be a heading or a hashtag, and a line starting with 1 could be a list or the year 1975. The server reads the answer one character at a time, waits when a line could go either way, and writes HTML once it knows.
Nothing the model writes is treated as HTML. Its text is escaped, and the server itself writes a short list of tags: paragraphs, headings, lists, code, bold, italic, and links. The page's security policy also blocks scripts outright, so a script that slipped through still wouldn't run.
Privacy and data
nojs.chat doesn't require an account or store your conversations on its servers. Conversation history travels with the page and is sent back when you submit your next message.
To generate a reply, we send your messages and conversation history to the AI provider (currently OpenAI GPT-6 Luna). Its own data policies apply.
Your conversation lives in your browser, inside the page, as a hidden field in the form. The server signs it so it can't be edited or forged. Signed doesn't mean encrypted: your history is readable in the page source. Long conversations get trimmed, and the page tells you when that happens.
The site also counts visits with a one-pixel image, without cookies, and keeps a short-lived count of requests per IP address in memory to limit abuse.
Keeping the bill small
A free AI chat with no sign-up is an open invitation to abuse, so there are a few limits in place: on how often you can ask, how long a question can be, and how long an answer can get. I won't go into the details, since they would mostly help someone trying to get around them.
The simplest safeguard isn't in the code at all. The API key has a spending cap of $1 a day at OpenRouter, the service that connects nojs.chat to the model. Whatever goes wrong, the worst case is a dollar. At current token prices, that has been plenty so far. If this ever lands on the front page of Hacker News, it probably won't be, and the chat will stop answering until the next day. That seems like a fair trade for a weekend project.
Where it should work
Any browser that can load a web page and submit a form should be able to use nojs.chat. That includes:
- E-readers: the Kindle's experimental browser, the Kobo web browser, PocketBook
- Text browsers: Lynx, w3m, Links, ELinks, and Emacs's eww
- Small browsers: Dillo, NetSurf
- Privacy settings: Tor Browser on its Safest setting, or any browser with JavaScript turned off
- Old or unusual devices: older smartphones, KaiOS feature phones, smart TVs, games consoles, car dashboards
- Proxy browsers: Opera Mini, which renders pages on Opera's own servers, so the answer probably arrives all at once instead of streaming in
If it works, or breaks, on something strange, I'd like to hear about it.
Old devices without modern HTTPS
Some old devices can't make a modern HTTPS connection, or no longer trust today's certificates. For those, the same chat is also at http://insecure.nojs.chat, over plain HTTP. Vercel only serves HTTPS, so Cloudflare answers the plain HTTP request and fetches the page from Vercel over HTTPS.
The name is the warning. Nothing between your device and Cloudflare is encrypted, so anyone on the network, such as a café's Wi-Fi or your internet provider, can read your questions, the answers and the conversation history, or change the page. Use it only when the normal site won't load.
Try it
Ask it something, ideally from the strangest browser you own. If an old device can't connect, try the plain HTTP address above.