An open-source AI character roleplay chat app β your own Character.AI alternative, running entirely in the browser.
Features β’ Getting Started β’ Architecture β’ Customization β’ Contributing
PlayWithU is a privacy-first, client-side AI roleplay chat app that lets you create, customize, and chat with AI characters β all without a backend. Your data never leaves your browser.
Bring your own API key (via OpenRouter), pick any LLM, and start roleplaying. No sign-ups, no subscriptions, no data collection.
- Real-time AI conversations with rich markdown rendering (bold, italics, code blocks)
- Message editing β edit any user message and regenerate the AI response from that point
- Response regeneration with candidate navigation β swipe through multiple AI responses for the same prompt
- Message deletion β remove individual messages from chat history
- Slash commands β
/resetto start over,/charactersto manage personas,/dbgfor debug tools
- Create custom characters with name, avatar, bio, description, scenario, and first message
- Dedicated character gallery at
/characterswith search, grid view, and active character indicator - Import / Export β share characters as JSON files or back up your entire collection
- Default character included β start chatting immediately with the built-in persona
- AI-powered chat memory β automatically summarizes your conversation into a structured memory block (NPCs, places, relationships, lore, timeline)
- Auto-summarize β triggers every 10 messages to keep context fresh without manual effort
- Memory snapshots β save, restore, and manage up to 10 memory versions
- Custom memory prompt β full control over how the AI structures its memory
- Dynamic placeholder system β use
{{char}},{{user}},{{char_description}},{{user_description}},{{scenario}},{{memory}}, and{{tools}}in your prompts - Create & manage multiple prompts β CRUD operations with the ability to switch between prompts on the fly
- Live preview mode β see how placeholders resolve before sending to the API
- Token counting β keep track of prompt size to stay within model limits
- Inject reusable prompt snippets into the
{{tools}}placeholder - Toggle tools on/off per conversation without deleting them
- Great for adding personality quirks, knowledge bases, or behavior rules dynamically
- OpenRouter integration β access hundreds of LLMs through a single API key
- Model selection β use any model available on OpenRouter
- Tunable parameters β temperature, max tokens, top-p, frequency penalty, presence penalty
- One-click parameter reset to sensible defaults
- Built-in debug modal (
/dbg) β inspect API request/response logs in real time - Full request tracing β see exactly what's being sent to and received from the LLM
- Dark theme β sleek, modern UI built for extended sessions
- Responsive design β works on desktop and mobile
- Persistent state β all data saved to
localStorage(survives page reloads) - Zero backend β no server, no database, no cloud β everything runs client-side
- Node.js 18+ installed
- An API key from OpenRouter
# Clone the repository
git clone https://github.com/neuezxc/playwithu.git
cd playwithu
# Install dependencies
npm install
# Start the dev server (Turbopack)
npm run devOpen http://localhost:3000 in your browser.
- Click the menu button (βοΈ) in the chat input area
- Open API Settings
- Paste your OpenRouter API key and set your preferred model ID
- Start chatting!
PlayWithU is a single-page Next.js 15 app (App Router) with no server components doing data fetching β everything is client-side.
app/
βββ page.js # Main chat page ("use client")
βββ layout.js # Root layout with Geist font
βββ globals.css # Global styles + Tailwind v4
βββ characters/
β βββ page.jsx # Character gallery page
βββ components/
β βββ SuperInput.jsx # Chat input with slash commands
β βββ InputMenu.jsx # Settings menu buttons
β βββ chat/
β β βββ ChatList.jsx # Message list with auto-scroll
β β βββ CharacterChat.jsx # AI message bubble (with regen/navigate)
β β βββ UserChat.jsx # User message bubble (with edit)
β β βββ CharacterImagePopup.jsx # Draggable character avatar
β βββ modal/
β βββ ApiSettingsModal.jsx # API key, model, parameters
β βββ CharacterModal.jsx # Create/edit characters
β βββ CustomPromptModal.jsx # System prompt editor
β βββ MemoryModal.jsx # Memory & summarization
β βββ PatternReplacementModal.jsx # Tools / prompt snippets
β βββ DebugModal.jsx # API request inspector
βββ store/
β βββ useCharacterStore.js # Characters, messages, pattern replacements
β βββ useApiSettingStore.js # API key, model, generation params
β βββ usePromptStore.js # System prompts (default + custom)
β βββ useMemoryStore.js # Chat summarization & snapshots
β βββ useChatStore.js # Message count tracking
β βββ useUserStore.js # User name/description (non-persisted)
β βββ useDebugStore.js # Debug logs
βββ utils/
βββ replacerTemplate.js # {{placeholder}} replacement engine
βββ textFormatter.js # Markdown/formatting utilities
βββ textUtils.js # Text helper functions
βββ formatChatForSummarize.js # Chat β summary formatter
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, Turbopack) |
| UI | React 19 |
| Styling | Tailwind CSS v4 |
| State | Zustand 5 (persisted to localStorage) |
| Icons | Lucide React |
| Markdown | react-markdown + rehype-raw |
| AI Provider | OpenRouter API (client-side fetch) |
| Font | Geist + Inter |
All stores use Zustand with persist middleware (saved to localStorage), except useUserStore which resets on reload.
| Store | Key | Purpose |
|---|---|---|
useCharacterStore |
character-storage |
Active character, character list, messages, pattern replacements |
useApiSettingStore |
api-storage |
API key, model ID, temperature, max_tokens, etc. |
usePromptStore |
prompt-storage |
Default + custom system prompts |
useMemoryStore |
memory-storage |
Summarization text, snapshots, memory prompt |
useChatStore |
chat-storage |
Message count for auto-summarize triggers |
useUserStore |
(not persisted) | User name & description |
useDebugStore |
(not persisted) | API request/response logs |
Characters are defined with:
| Field | Description |
|---|---|
| Name | The character's display name |
| Avatar URL | Image URL for chat bubbles and gallery |
| Bio | Short tagline shown in the character card |
| Description | Detailed personality and appearance (supports {{user}} placeholders) |
| Scenario | World-building context injected into the system prompt |
| First Message | The opening message when a chat starts |
Use the Custom Prompt editor to write your own system prompts with dynamic placeholders:
You are roleplaying as {{char}}. The user is {{user}}.
{{char_description}}
Scenario: {{scenario}}
Memory: {{memory}}
Tools: {{tools}}
All placeholders are replaced with real values before the prompt is sent to the API.
Add reusable prompt snippets that get injected into {{tools}}:
- Toggle them on/off per conversation
- Great for adding situational rules, knowledge, or personality modifiers
- Changes take effect immediately β the system prompt is refreshed on every toggle
| Command | Action |
|---|---|
/reset |
Clear chat history and memory, start fresh |
/characters |
Navigate to the character gallery |
/dbg |
Open the debug inspector modal |
npm run dev # Start dev server (Turbopack)
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLintContributions are welcome! This is a JavaScript-only project (no TypeScript).
- Fork the repo
- Create a feature branch (
git checkout -b feat/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feat/amazing-feature) - Open a Pull Request
- No semicolons (unless needed for disambiguation)
- Single quotes for strings
- 2-space indentation
===only, never==handleXxxnaming for event handlers- Named exports for components
This project is open source. See the repository for license details.
Built with β€οΈ by neuezxc