Pocket Pixel is a gamified personal finance tracker built for people who want to make budgeting actually fun. Inspired by retro RPG aesthetics, it wraps your income and expenses in a pixel-art UI where categories become Vaults, habits become Quests, and every transaction is part of your financial adventure.
- Vaults — organize spending into themed buckets (food, rent, subscriptions, anything)
- Recurring Quests — automate repeating transactions on daily, weekly, monthly, or yearly schedules
- Tags — label transactions with custom icons and colors for granular analytics
- Analytics — monthly and yearly breakdowns, tag-based insights
- Profiles — pick your avatar and make it your own
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18, TypeScript, Tailwind CSS |
| Backend | Express.js, TypeORM, SQLite (better-sqlite3) |
| Auth | JWT (30-day sessions), bcryptjs password hashing |
| Scheduling | node-cron (recurring transaction automation) |
| Validation | Joi |
| Process Manager | PM2 |
| Monorepo | npm workspaces |
This project was entirely vibe coded with claude, gemini models, and codex. The UI was designed with Google stitch. It is full of sloppy code and possible bugs with many features still under development.
Google stitch: https://stitch.withgoogle.com/projects/14795631199624450898
The repository has a bunch of skills/rules defined in .agents or .claude folders. These are supported by codex and gemini models.
pocket_pixel/
├── packages/
│ ├── api/ # Express REST API + TypeORM entities
│ │ └── src/
│ │ ├── entities/ # User, Expense, Vault, Tag, TransactionTag
│ │ ├── routes/ # auth, users, transactions, vaults, tags, recurring, analytics
│ │ ├── middleware/ # JWT auth, error handling
│ │ └── scheduler/ # node-cron recurring job manager
│ │
│ ├── ui/ # Next.js frontend
│ │ └── src/
│ │ ├── app/ # Dashboard, Profile, Stats, Auth pages
│ │ ├── components/ # Modals, AppBar, Nav, UI primitives
│ │ └── lib/ # API clients, helpers, icon mapper
│ │
│ └── shared/ # Shared TypeScript types/interfaces
│
├── ecosystem.config.js # PM2 production config
├── tsconfig.base.json
└── package.json # Workspace root
- Node.js ≥ 18
- npm ≥ 9
git clone git@github.com:ali-ahnaf/pocket_pixel.git
cd pocket_pixel
npm install
npm run build:shared # builds shared dependencies
npm run migration:run # creates/updates the .sql fileCreate .env files for both the api and the ui
- Copy
.env.exampleto.envfor both the api and the ui - Fill the .env files with the appropriate values (or keep the defaults for local development)
Run the API and UI in separate terminals:
# Terminal 1 — API (http://localhost:4000)
npm run dev:api
# Terminal 2 — UI (http://localhost:3000)
npm run dev:ui# API tests
npm run test:api
# UI E2E tests
npm run test:e2e# Build shared → UI → API in order
npm run build:prod
# run migrations and saves the file in /var/www/pocket_pixel
npm run migration:run-prod
# Start the server (API serves the compiled UI)
pm2 start ecosystem.config.js
# → http://localhost:4000All endpoints are prefixed with /api. Protected routes require an Authorization: Bearer <token> header.
SQLite database managed via TypeORM with migrations.
Run migrations:
npm run migration:run # Apply pending migrations
npm run migration:generate # Generate migration from entity changes
npm run migration:revert # Roll back the last migrationThe API can snapshot the SQLite database and upload it to Cloudflare R2 (an S3-compatible object store) every 12 hours. It's off by default — flip ENABLE_BACKUP=true in the API .env to turn it on. Backups use SQLite's online backup API, so snapshots stay consistent even while the app is writing.
Each run uploads an object named pocket_pixel/pocket_pixel-<timestamp>.sqlite to your bucket.
Why R2? The free tier includes 10 GB storage and, unlike most clouds, zero egress fees — you can pull your backups down for free. That's plenty for a SQLite file.
- Sign up (or log in) at dash.cloudflare.com — the account is free.
- In the sidebar open R2 Object Storage and click Enable R2. Cloudflare asks for a payment method to verify identity, but you are not charged while you stay within the free tier limits.
- Go to R2 → Create bucket.
- Name it (e.g.
pocket-pixel-backups) and create it. The location hint can stay on Automatic. - Remember this name — it's your
R2_BUCKET.
- On the R2 overview page click Manage R2 API Tokens → Create API Token.
- Permission: Object Read & Write.
- Scope it to the bucket you just created (recommended), then Create.
- Copy the Access Key ID and Secret Access Key — the secret is shown only once.
Your Account ID is on the R2 overview page (and in the S3 endpoint Cloudflare shows: https://<ACCOUNT_ID>.r2.cloudflarestorage.com). Copy it.
Add these to packages/api/.env (see packages/api/.env.example):
ENABLE_BACKUP=true
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key-id
R2_SECRET_ACCESS_KEY=your-secret-access-key
R2_BUCKET=pocket-pixel-backupsRestart the API to pick them up (pm2 restart ecosystem.config.js in production, or restart npm run dev:api locally). On boot you should see Database backup scheduled every 12 hours to R2 in the logs. If R2 credentials are missing while ENABLE_BACKUP=true, the scheduler logs a warning and stays idle instead of crashing.
- Download the desired
.sqliteobject from your R2 bucket (Cloudflare dashboard or any S3 client). - Stop the API.
- Replace the live database file (
/var/www/pocket_pixel/pocket_pixel.sqlitein production, orpackages/api/pocket_pixel.sqlitelocally) with the downloaded file. - Start the API again.
Organize your money into custom buckets — think of them as tagged envelopes. Each vault has a name, icon (from Lucide), background color, and can be marked as your default. Transactions without a vault fall into the default one.
Set a transaction to auto-repeat on a schedule (daily / weekly / monthly / yearly). The API scheduler restores all active quests on startup using node-cron, so nothing gets missed between restarts.
Three views to understand your spending:
- Tag breakdown — which labels are eating your budget
- Monthly report — income vs. expenses by month
- Yearly report — long-term trend across all months
Contributions are what make open source awesome. All skill levels welcome — whether it's fixing a typo, adding a new feature, or improving the docs.
- Fork the repository
- Create a feature branch
git checkout -b feat/your-feature-name
- Make your changes — keep commits focused and descriptive
- Test your changes locally (both
dev:apianddev:ui) - Push your branch and open a Pull Request
- Prettier is configured
- TypeScript strict mode is enforced
- Keep components small and single-purpose
- Name things clearly — no abbreviations unless obvious
- Do not make changes in the file that are not relevant to the task at hand.
Open an issue with:
- What you expected vs. what happened
- Steps to reproduce
- Your OS and Node.js version
╔═══════════════════════════════════════════════════╗
║ ★ P A R T Y R O S T E R ★ ║
║ These brave heroes joined the quest to slay ║
║ the dreaded budget-goblins of Pocket Pixel. ║
╚═══════════════════════════════════════════════════╝
🗡️ Want to join the party? Grab a quest from the issue board and roll for initiative.
MIT — do whatever you want with it. See LICENSE for details.
Made with ☕ and a lot of pixel art inspiration.