← All work

Artificial Italian

A full Claude chat app with a South Jersey accent. You talk to Claudio, a retired machinist, or his wife Claudia, a retired seamstress and the best cook in the family. Both have opinions about your gravy.

Year
2025, rebuilt in 2026
Made with
HTML, CSS, vanilla JS, Firebase (Functions, Auth, Firestore, Storage), Stripe, Claude Sonnet 5.5 with web search and fetch
Links
Live siteSource on GitHub
Illustrated portraits of Claudio in a black flat cap on sage green and Claudia in gold-rimmed glasses on tomato red, next to the words 'Claudio & Claudia: Artificial Italian' and a small green, white and red bar
The tagline is still the whole pitch.
Why

This was my first time playing with prompt building. I'm Italian-American, wrapping a language model in that heritage sounded like fun, and Claude → Claudio was too on the nose not to do.

The first version was a portrait, a text box and a send button. Its whole personality lived in the system prompt, and it couldn't do much besides talk about pasta. The rebuild makes it a real chat app that can do anything Claude can, in his voice. Then it got a second character, and the portraits got redrawn from photos of my late grandparents.

How it works

The page is still plain HTML, CSS and a handful of ES modules, no framework and no build step. It looks like any chat app: past chats in a sidebar, a thread, and a composer that takes images, PDFs and text files. Under the hood it's one Firebase Function making one streaming call to Claude Sonnet 5.5 with adaptive thinking, web search, web fetch and prompt caching.

Your message→claudioChat→messages.stream→Server-sent events→The thread

Two characters

A toggle next to the name switches between Claudio and Claudia. Each has a backstory, a system prompt, an avatar and an accent color: sage green for him, tomato red for her. Their trades only come up when a question is near them. Ask about a leaky faucet and you'll hear about the shop floor; ask about recursion and you just get a good explanation of recursion.

A conversation stays with whoever it started with. Toggling mid-chat starts a new chat with the other one and keeps whatever you'd typed.

Replies that keep going

The function streams thinking, text, a line each time he searches or reads a page, and the sources he cited. A reply belongs to its chat, not the screen. Switch chats mid-answer and it keeps streaming in the background and saves when it's done. Come back while it's still going and the chat catches up and continues live.

Chats live in IndexedDB. For signed-in users they're mirrored to Firestore, one document per message, so they follow you to another device. A message too big for a document, like a large image or a long fetched page, goes to Cloud Storage instead.

Who pays

Every visitor is signed in anonymously in the background, and gets $0.30 of actual API cost on my key, about ten messages. There's no counter; you chat until it runs out. The budget is per network over 30 days, so clearing storage doesn't buy more. After that, you can bring your own Anthropic key or pay $6 a month for a $5 allowance that resets on your billing date.

Each reply's cost is worked out from the token and search counts the API reports. That's what gets charged against the trial or the allowance, so a message that fails before he says anything costs nothing.

Subscription→Key from the browser→Saved key→Free trial

Saved keys are encrypted with AES-256-GCM, with the user's ID bound in, so a copied ciphertext won't decrypt on another account. The browser only ever sees the last four characters.

Screens
The Artificial Italian chat page: a sidebar with Claudio's avatar, a character toggle and a green New chat button, and a cream main area with his round portrait, a greeting, four suggested prompts and a 'Talk to Claudio' composer with a 'Think it through' button
ai.noahgdorfman.com. Claude writes a new batch of greetings and suggestions every day.
Screen-print style illustration of Claudio: black flat cap, deep smile lines, black jacket, on a sage green and cream background
Claudio, in my great-grandfather's flat cap
Screen-print style illustration of Claudia: grey curls, gold-rimmed tinted glasses, a wide smile and a white collar, on a tomato red and cream background
Claudia, who will ask if you've eaten
What it taught me
  1. 1

    Server tools change the response shape

    In v1, web search split the reply into a dozen blocks and content[0] was Claudio's private thoughts, not his answer.

  2. 2

    Search is billed in input tokens

    One chicken-parm question ran two searches and about 49k tokens.

  3. 3

    Let the model do the character work

    v1 hid its reasoning in tags and re-paid for it every turn. The rebuild uses real thinking, and the reply is just the reply.

  4. 4

    A reply belongs to its chat

    Tying the stream to the chat instead of the screen is what made switching mid-answer safe.

  5. 5

    Charge what it actually cost

    A dollar budget from the API's own usage counts is fairer than a message count, and failed replies are free.

  6. 6

    Don't trust the first forwarded address

    Only the last X-Forwarded-For entry comes from Google's front end. The rest can be spoofed.

Stack
FrontendStatic HTML, CSS and ES modules; marked, DOMPurify and highlight.js from jsDelivr
HostingGitHub Pages with a custom domain
BackendFirebase Functions v2 (onRequest), Node 22: streaming chat, account API, Stripe webhook
ModelClaude Sonnet 5.5 with adaptive thinking, web search and web fetch
Auth and dataFirebase Auth (anonymous, then Google), IndexedDB, Firestore, Cloud Storage
PaymentsStripe Checkout, Customer Portal, webhooks