Orin

AI Companion

Jul 2026
View work

Orin is a voice-enabled AI companion I built as a side project. You can text it or call it, and both modes share the same conversation thread.

Orin cover image

Orin cover image

Orin is meant for conversation. You open a chat, say what's on your mind, and get a streaming reply. If talking out loud is easier, you can start a voice call in the same thread. What you said by text is still there when you hang up. What you said on the call shows up in the chat as it happens.

Orin demo

I designed, built, and shipped the whole thing myself. Frontend, backend, voice pipeline, and the personality system. Most of the implementation was done with Cursor as my editor and pair programmer.

One thread, text or voice

At a product level, Orin is a chat app with a voice call built in. You start from /new, pick a suggestion or type freely, and the conversation streams back in real time.

New chat with suggestion chips

New chat with suggestion chips

Enter to send, Shift+Enter for a new line, Escape to stop a reply mid stream. You can edit your own messages, retry a response, copy text, or use read aloud on individual replies.

Keyboard shortcuts

Keyboard shortcuts

Voice is a call overlay on the chat you're already in, not a separate app mode. Tap the voice icon and talk. In inline mode, the transcript appears live in the thread below as you speak.

Voice call in inline mode

Voice call — inline

Switch to fullscreen and the chat falls away. An opaque overlay with a 3D orb reacts when you or Orin is speaking. No transcript on screen; you're just in the conversation.

Fullscreen voice call with reactive 3D orb

Voice call — fullscreen

Mute, switch between inline and fullscreen, or end the call from either mode. When it's over, the full transcript is already saved in the thread. Voice turns are marked so you can tell them apart from typed messages.

Full conversation thread

Full conversation thread

You can try Orin without signing in. Anonymous users get a small allowance in the browser. Sign in with Google to save history, sync across devices, and unlock voice calls.

Sign up with Google

Sign up with Google

After sign in, onboarding walks you through personality, custom instructions, voice, and speaking speed.

General settings

General settings

Everything lives in Settings after that: theme, bubble layout, five personality styles, API key management, data export, account deletion.

There's a free platform allowance backed by my API keys. When it runs out, signed in users can add their own OpenAI and ElevenLabs keys to keep going. No billing flow. I wanted the product to be usable without integrating Stripe for a side project.

Usage and API keys

Usage & API keys

ElevenLabs as the starting point

I built Orin because I wanted to explore voice AI, especially what ElevenLabs makes possible with real time speech. The APIs are good and the space is moving fast. I was curious what a product built around that would actually feel like to use.

Orin landing page light mode

Landing page — light mode

Conversational AI seemed like the most interesting thing to build. I wanted the whole app to revolve around talking and listening. ElevenLabs fit well. Their voices carry real expression, which you notice when someone is speaking back to you in real life.

Orin landing page dark mode

Landing page — dark mode

In the app, I used Scribe v2 Realtime for dictation, Eleven v3 for read aloud, and Eleven v3 Conversational for realtime calls through Speech Engine.

Editing a user message

Editing a user message

Phases, stack, and the prompt system

I broke the build into phases and shipped each one before moving on. Foundation and schema first, then text chat with streaming and persistence, then voice calls with a live transcript in the thread, then customization, then auth, quotas, and bring your own keys, then tests around the parts that could break in production. Working in order kept the scope honest.

Home screen dark mode

Home screen — dark mode

On the product side I made deliberate cuts early. Conversation only. No agents, no tool use, no file uploads. Text and voice in one thread. A limited free tier instead of payments.

About Orin page

About page

Architecture

The core idea was one brain, two surfaces. I used the Vercel AI SDK for all LLM calls across text and voice. ElevenLabs Speech Engine handled audio in and out over WebRTC, but not the reasoning. I didn't use ElevenLabs' bundled conversational agent because that would have meant two prompt stacks that could drift apart. I deployed the Next.js app on Vercel and ran voice in a Node sidecar in the same repo, because a voice call needs a WebSocket that stays open. Serverless functions weren't a fit for that. Locally I run the sidecar with ngrok to expose the WebSocket; in production I deployed it on Railway. The sidecar imports the same lib/ai modules as /api/chat, so there's one code path for generation. They coordinate through Supabase.

Terms of Service page

Terms of Service

Stack

Next.js and the App Router for the web app and API routes. Supabase for Google auth, Postgres, row level security, and Realtime during calls. Nexus UI for the chat thread, composer, and message primitives. shadcn/ui for settings and the rest of the app shell. OpenAI through the AI SDK for text responses. ElevenLabs for Speech Engine, Scribe dictation in the composer, and read aloud. Vitest for quotas, key encryption, auth redirects, and message sanitization.

Design and frontend

I handled the visual design, layout, and interaction details myself. The call overlay, streaming message states, sidebar history, favorites, title editing. I cared about how transitions felt when opening panels, or sending a message, or during a call and how the thread stayed readable when voice and text were mixed. Small things, but they matter a lot.

Dictating a message

Dictating a message

The search modal, settings, and recent chats in the sidebar were the other components I cared much about. They needed to feel as lightweight as the thread itself.

Search chats modal

Search chats

Cursor

I used Cursor throughout for speeding up implementation. Scaffolding API routes, wiring Supabase, iterating on the voice handler, refactoring shared AI modules. It was especially useful on the sidecar setup and the glue between ElevenLabs session metadata and conversation IDs. I still made all the architectural calls. Cursor helped me move through the code faster.

Privacy Policy page

Privacy Policy

Prompt engineering

Getting consistent personality across text and call was mostly a prompting problem, instead of a model problem. This was the most important problem I had to solve. I didnt want Orin sounding robotic or like a generic chatbot.

Warm personality in text chat

Warm personality in text chat

Initially I tried by writing short system prompts for each personality. But there was an issue. It takes more than just telling a model to sound natural for it to actually sound natural. I did some research and found that the best way to get a model to sound natural is to be very detailed and give it a lot of examples of how people actually speak. Eventually I was able to get to a point where the responses matched the personalities and were concistent even after many turns.

Personalization settings

Personalization settings

Each personality (Warm, Curious, Playful, Calm, Direct) is a markdown document describing how Orin should respond: pacing, how much it explains, when it asks questions, how it handles disagreement. A shared core doc set baseline identity and guardrails. At runtime I assembled the system prompt in layers: core, personality doc, runtime context (user name, custom instructions, mode), and a voice specific addendum on calls. The same assembly ran in /api/chat and in the voice sidecar, so text and voice read from the same source.

What's next

Orin does what I set out to build, so I'm leaving it alone for now. I'll fix bugs when they show up and add things when something feels worth doing.

I enjoyed this project. I learned a lot about voice models, prompting, running a backend that handles both text and realtime audio, and making product decisions on my own.

Orin is live at orin.chat. The source is on GitHub under MIT. I'd love for you to try it out and let me know what you think. Once you've exhausted your credits you can add your own OpenAI and ElevenLabs keys to keep going.

Keep building!