Rick Sanchez.Ai
- 6 Devlogs
- 31 Total hours
An html file where you paste your own API key and get a once in a lifetime opportunity of talking with the smartest man in the multiverse Rick (c-137)
An html file where you paste your own API key and get a once in a lifetime opportunity of talking with the smartest man in the multiverse Rick (c-137)
Fully over hauled the UI and added a pixel art intro story.
This phase added the startup mascot — got Rick & Morty standing above the portal in the intro screen using pure CSS. Experienced a little issue with the alignment which I discovered later. There was still a hanging translateX(-50%) that was pushing the mascot aside. Removed the line of code and got the exact alignment with the help of the sliders (X, Y, size). Got to the desired values translate(60px,-138px) scale(0.76) and removed them right away. Rewrote the intro animation logic as well — it automatically skips after some seconds, now it will play when loaded and wait for a tap to proceed. I have experimented with making a window similar to the macOs one (traffic lights and title bar) but did not get it in time, so I reverted it.
The last part was related to the sidebar and its Pickle Rick icon. Made the sidebar collapsed into the narrow column with the rotating portal ring logo which opens and closes it. Updated the Pickle Rick image by making the design more precise (added his body, spots, teeth and tongue) and got the exact location using the same sliders technique. Made Rick’s face fade into the black instead of clashing. Last thing hooked it into the existing “annoyed” mood detection,
Phase 4 Devlog — Deployment & Voice Reliability
Phase 4 was less about the face and more about getting the thing actually reachable and sounding right. Three separate problems, three separate fixes.
1) GitHub Pages was never going to work
Deployments sat in deployment_queued and timed out, over and over, on totally unrelated repos. Turned out it wasn’t a config issue at all — GitHub’s status page had an active incident open the same day (“Incident with Pages,” degraded performance, investigating). No amount of poking at Settings → Pages or the Actions tab was going to fix an outage on their end. Moved hosting off Pages entirely rather than wait it out.
2) The Fish Audio proxy needed its own home
The app calls out to Fish Audio for Rick’s voice, but the API key can’t be trusted client-side and Fish’s endpoint doesn’t play nice with browser CORS anyway. Solved both with a small Cloudflare Worker that does one job: accept a POST to /tts with fishKey, text, and a few optional params (reference_id, format, model), forward it to Fish Audio, and hand back the raw audio bytes.
The frontend already had the right shape for this call, so it was mostly a matter of getting the Worker’s workers.dev subdomain toggled on — it silently doesn’t activate until there’s an actual deployed script behind it, which cost a bit of head-scratching before the first real deploy went through.
3) Voice playback was getting blocked, then falling out of sync
Two separate bugs stacked on top of each other here.
audio.play() was firing after an await fetch(), and by then Chrome no longer considered it tied to the user’s original click — so playback silently failed with a permission error. Fixed by unlocking the page’s audio system on the very first click/keypress/tap anywhere, which browsers treat as valid for the rest of the session regardless of how much async work happens in between.
Once audio was actually playing, timing was off. There’s a fallback timer that force-reveals Rick’s reply text if the voice hasn’t started yet, originally set to 6 seconds. Fish Audio’s real-world response time runs closer to 8 seconds, so text was popping up two seconds before the voice caught up — reply and audio landing noticeably out of step. Bumped the fallback specifically for Fish voice mode to 15 seconds, leaving the faster browser-TTS and text-only modes untouched since they never had the delay problem to begin with.
Net result
The app deploys somewhere that isn’t at the mercy of a GitHub outage, the voice proxy has its own dedicated backend instead of leaking a key client-side, and audio no longer gets blocked or races ahead of the text it’s supposed to accompany.
Phase 3 Devlog — Face & Layout Polish Phase 3 added a handful of new facial states on top of the existing set (talking, angry, blink, thinking, sleep, surprised): manic, disgust, and exasperated. Each is just CSS — scaling the unibrow, eyes, and mouth per state and letting the existing transition properties handle the interpolation. They’re wired into guessMood(), so the reply text picks which one fires.The main bug was in the pupils. Bumping their size seemed trivial, but the original CSS used a multi-offset box-shadow to fake small catchlight dots around the pupil, and none of those shadow values had an explicit color. Browsers default an unset box-shadow color to currentColor, which here was the page’s neon-green text color. Scaling the pattern up didn’t just make the pupil bigger, it made a glowing green blob visible around it. Fixed by dropping the multi-dot trick for a single solid pupil with one inset highlight — simpler, and it scales cleanly.There was also a first pass that auto-cycled through every state on a timer so they’d show up without an API key connected. It conflicted with the actual reply-driven logic, so it got pulled — states now only ever fire off a real message, same as talking and surprised always did.A second bug turned up after that: the auto-blink loop was resetting the container’s full class list on every blink, which incidentally wiped out whatever mood state a reply had just set. Since blinks fire every 2–6 seconds, this meant states were getting cut short or skipped entirely. Fixed by having blink toggle its own class instead of resetting the container.Smaller changes: the chat history panel moved to the leftmost column, the unibrow got nudged up slightly to give the bigger pupils more room, and the inaccurate relative timestamps (“3m ago”) on chat list items were removed in favor of just showing message count.
Phase 2 Devlog — Voice IntegrationPhase 2 was all about giving Rick his actual voice using Fish Audio, which hosts a Rick Sanchez voice clone model. The first problem hit immediately: Fish Audio had quietly updated their API. The old speech-1.5 model name passed inside the request body no longer worked — Fish Audio now expects the model as an HTTP header (model: s2.1-pro-free), and the model names themselves had changed entirely. Once we figured that out and stripped the invalid body parameters, the request was clean. But Rick still wasn’t speaking.The bigger wall was CORS. The moment a browser fetch() call sends an Authorization header to a third-party API, the browser fires a preflight check — and if the server doesn’t explicitly whitelist your origin, the request gets blocked before it even leaves the browser. Fish Audio’s API is designed for server-to-server use, so no frontend origin gets through, whether you’re on localhost or a real GitHub Pages domain. There’s no client-side fix for this. As a temporary workaround we wired in the browser’s built-in speechSynthesis API, tuning the pitch down and slowing the rate to approximate Rick’s drawl. It worked, but it obviously wasn’t Rick.The real fix was a Cloudflare Worker acting as a one-file serverless proxy. The insight is simple: CORS only applies to browser-to-server calls, not server-to-server. So the browser calls the Worker (which is on your own domain, no CORS issue), the Worker forwards the request to Fish Audio privately, and streams the audio blob back with open CORS headers. The whole proxy is about 60 lines of JavaScript, deploys in two minutes from the Cloudflare dashboard for free.
Phase 1 is a fully functional browser-based AI chat interface that lets you have conversations with Rick Sanchez from Rick and Morty, built entirely as a single self-contained HTML file with no build tools, frameworks, or external dependencies beyond the APIs. The interface combines a pure CSS pixel-art Rick portrait—constructed entirely from positioned divs using borders, border-radius, transforms, and box shadows—with Google’s Gemini API to create a character that not only responds intelligently but visibly reacts to the conversation. Rick can talk, blink automatically, become angry, smirk, think, look surprised, or fall asleep, with each expression driven by smooth CSS transitions triggered by heuristic mood detection on his responses. When idle, he periodically delivers impatient one-liners that pause automatically while API requests are running. Every Gemini request includes a carefully crafted system prompt that keeps Rick in character, encouraging him to insult trivial questions, enthusiastically explain genuinely interesting ones, naturally reference the Rick and Morty universe, and never acknowledge being an AI. The chat interface features animated message bubbles, an auto-expanding input box, typing indicators, split-panel layout, a live speech bubble above Rick displaying his latest response, and a model selector that allows switching between Gemini models without losing conversation history. An optional Google Custom Search integration detects questions requiring current information, silently injects search results into the prompt, and lets Rick answer as though he already knew the latest news without breaking character. The visual style embraces a neon green CRT terminal aesthetic with scanlines, portal glow effects, handwritten fonts for Rick’s dialogue, and responsive animations throughout. API keys remain only in browser memory for the current session, no data is stored, and the entire application can be launched by simply opening a single HTML file.