Software developer · Mumbai, India
Amey Pacharkar
I build games and tools that teach.
Computer engineering graduate (BE, May 2026) working with Flutter, full-stack web and AI/ML. I'm open to roles at startups.
- Available now
- Remote preferred, flexible hours
- Open to Mumbai, Pune or relocation
- Open to Flutter, full-stack web or AI/ML roles
About
I'm Amey Pacharkar, a computer engineering graduate (BE, May 2026) from Mumbai. I like building niche, slightly weird software, and I'm interested in psychology and game theory. I've worked on Flutter apps for Android and iOS, full-stack web projects and AI/ML libraries.
My favourite project is Chem Arcade. I teach chemistry for CET and JEE to students in the Ambegaon area, and I wanted them to enjoy it, so I built a set of chemistry games with weekly and all-time leaderboards. Each one is made so a student who stumbles on it can have a good time with friends or on their own.
At work I built ChatLink, an internal Android app that automates bulk messaging. I took it from idea to deployment and have supported it for almost a year. I have also built features and systems for three web projects at my current company.
I'm looking for a chance to contribute to live projects.
Featured projects
Four projects I can explain end to end: what problem they solve, how they are built, what went wrong and what I would change.
Chem Arcade
Browser chemistry games for CET, JEE and board-exam students
Solo project · Private repo · 106 commits
The problem and the idea
Students learn best when they're having fun. I teach CET and JEE chemistry and wanted my students to enjoy it, so I built a set of chemistry games anyone can open and play, alone or with friends. There is a competitive side too, with all-time, daily and weekly leaderboards.
The games
- Periodic Table Battleship: played locally or online with a room code (no matchmaking yet).
- Name It: a solo, timed IUPAC naming game with a 25-level Learn mode and a competitive Endless mode (Easy, CET, JEE). Your score rises the higher you climb, the faster you answer and the more you get right.
What I owned
- The game logic for both games.
- All the chemistry content and levels.
- The UI design: my own idea and first draft.
How I checked it
- The level-scoring formulas started as AI output. I tested them and rebalanced them to make them fairer.
- The Firestore security rules, which act as the anti-cheat layer, were written with AI. I tested them, along with the app, using Firestore rules tests and Playwright end-to-end tests.
Privacy by design
- Most players are under 18, so there are no emails and no ads. Players join with a nickname and a PIN, never a real name.
Student opens the siteBrowser
An installable web app (PWA) built with Next.js
Joins with a nickname and PINFirebase
No email, no real name
Plays a gameNext.js
Game logic runs in the app
Scores and rooms are savedFirestore
Security rules check every writeFirestore rules
The anti-cheat layer
LeaderboardsFirestore
All-time, daily and weekly
What I'd do differently
- Move Name It's user data out of Firestore into a separate relational database such as Postgres, for tighter control over where student data lives.
- Next.js (App Router)
- React
- TypeScript
- Tailwind
- Firebase (Firestore + Auth)
- PWA
- Playwright
- Vercel
WatchDog
Website change monitoring: get emailed a diff when a page changes
Solo project · Private repo · 38 commits
The hosted demo is offline, so screenshots and a recording will be shown here instead.
The problem and the idea
You add a web page to watch. WatchDog checks it on a schedule and, when the visible content changes, emails you a colour-coded diff of exactly what changed. The idea was to keep an eye on a rival's website, a stock page or any other page. I built it as a subscription product but never marketed it, so it has no customers.
What I built
- The whole stack: React dashboard, Express API, scheduler, scraper, diffing and email alerts.
- Subscription plans with Lemon Squeezy: Free (1 monitor, daily checks), Basic $9 (10 monitors), Pro $19 (30 monitors, hourly checks) and Agency $49 (unlimited monitors, hourly, 6-hourly or daily checks).
- Users are identified by email only, with no passwords or sessions.
Problems I solved
- Cooldown bug: SQLite stores UTC times with no timezone marker, and JavaScript read them as local time. In India that made a 5.5-hour error, bigger than the Pro and Agency cooldowns, so users could bypass them. Fixed by forcing UTC when parsing.
- Email outage on Railway: Node's IPv6-first DNS resolution caused ENETUNREACH errors when sending mail. Fixed by resolving the mail server to IPv4 and keeping the TLS server name intact.
- Webhook signatures failing: express.json() re-formatted the body, which invalidated the HMAC signature. Fixed by reading the raw body on the webhook route only, and comparing signatures in constant time.
- Noisy pages: the scraper strips scripts, styles, SVGs and meta tags so a diff only shows text a person would read, and it sends browser-like headers to get past basic bot blocks.
Hourly job finds due monitorsnode-cron
Based on each plan's check frequency
Fetch the pageAxios
Browser-like headers, 15-second timeout
Clean the textCheerio
Strip hidden and non-visible content
Hash and compareSQLite
First check saves a baseline; later checks compare hashes
Diff the changesdiff
First 10 changed lines saved to the alerts table
Save, then emailNodemailer
Database updated first; the email sends in the background
What I'd do differently
- Email-only access means anyone who types another person's email can see that person's monitors. I'd add magic-link authentication.
- Scraping runs inside the API process, so a restart or a slow scan can miss checks. I'd move it to a job queue with a separate worker, and PostgreSQL.
- Cheerio only sees static HTML, so client-rendered single-page apps come back empty. I'd add a headless-browser fallback and CSS-selector targeting.
- React (Vite)
- Tailwind v4
- Node.js
- Express
- node-cron
- SQLite
- Axios
- Cheerio
- Nodemailer
- Lemon Squeezy
- Railway
GhostFS
Ephemeral encrypted file transfer that hides data in video frames
Solo project · Public repo · 11 commits
The relay server sleeps when nobody is using it, and waking it takes long enough that the first connection can time out. If that happens, wait a minute and try again. A recording of a full transfer is shown below.
The problem and the idea
A for-fun project. After learning about bits, bytes and the least significant bit (LSB) in my third-year computer networks course, I wanted to see if I could hide data inside pictures and video. It grew step by step: text to bits drawn as black and white pixels, then bits hidden in an image's LSB, then the same in video frames, then a live transfer system on top.
What I built
- The byte-to-pixel encoding and the LSB hiding, written by me.
- The relay and channel system were built with AI help, and I can explain how they work.
Design decisions
- Encryption (AES-256-GCM, WebCrypto) runs in the browser, so the relay never handles plaintext.
- The key sits in the part of the link after the #, which browsers never send to a server. The relay only ever sees the channel name.
- The relay is stateless: nothing is stored in a database. A channel holds two people, waits 15 seconds after the transfer for the download, then destroys everything.
- Frames travel as raw uncompressed pixels over WebSocket. Lossy video compression would destroy the hidden bits, so I avoided it, and TCP delivers them intact.
Sender picks a fileBrowser
Encrypted with AES-256-GCM in the browser
Bits hidden in framesCanvas
One payload bit in the lowest red bit of each pixel of a 1280×720 decoy video frame
Link is createdBrowser
#channel=…&key=… The key never leaves the link's # part
Raw frames stream through the relayElixir/Phoenix
Forwarded unchanged, nothing stored
Receiver joinsBrowser
Reads the lowest red bit of every pixel, rebuilds and decrypts
Channel destroyedRelay
15 seconds after the transfer ends
What I'd do differently
- Cold start: the free-tier Render relay sleeps when idle and takes too long to wake, so the first connection times out. I'd keep one relay machine always on, or make the frontend show a 'waking the relay' status and retry.
- Bandwidth: raw 720p frames are about 2.8 MB each, over a gigabit per second at full frame rate. That is fine on localhost but not on a real network. I'd send smaller or fewer frames and compress the payload first.
- No backpressure: the sender never checks how much is queued, so a slow connection piles frames up in browser memory. I'd check bufferedAmount before sending.
- Detectability: a raw RGB stream isn't a normal video stream, so traffic analysis would spot it. The real protection is the encryption and the key staying out of the server's reach. The LSB hiding is the technique I wanted to explore.
- Vanilla JS
- Canvas API
- WebCrypto
- Elixir/Phoenix (WebSocket relay)
- Vercel
- Render
multi-agent-experiment
Multi-agent LLM games built from raw HTTP calls, with no frameworks
Solo project · Public repo · Python + local llama3.2 (Ollama)
The problem and the idea
I wanted to learn how multi-agent LLM systems work by building them from raw HTTP calls upward, one concept per game. It was a learning project, not a controlled study, so I only claim what I directly observed.
What I built
- Four games, each adding one idea, up to a Werewolf game run by a real state machine: Optimist vs Pessimist, Twenty Questions, Spyfall and Werewolf.
- Every agent runs on the same small local model.
What I observed
- Memory is a list you resend: a second call without the history knows nothing, and a call with the history remembers. Easy to reproduce with curl.
- A system prompt sets tone: a pirate prompt changed the output at once.
- Don't let the model judge what code can check: in Twenty Questions the answering agent said Yes to a wrong guess and the agents slid into fake small talk. Moving the correctness check into Python as a string comparison fixes it by construction.
- Contradictory prompts cause invented facts: a lone werewolf told both that it had no fellow wolves and to discuss with them invented a second wolf. After I branched the prompt the next run didn't. That's one run before and one after, so it's consistent with the contradiction being the cause, not proof.
- Position bias, with caveats: in Spyfall voting one agent name got about half the votes regardless of its role (18 of 36 in the first batch). After shuffling it still got about 10 of 24, above the 25% chance level. I tallied this by hand and changed the name order and the speaking order at the same time, so I couldn't tell which mattered.
State machine picks the speakerPython
And decides what that agent is allowed to see
Prompt is builtPython
Role, rules and the history so far
HTTP call to the local modelOllama (llama3.2)
Reply is checked by codePython
For example, is the guess correct?
State updates, next turnPython
What I'd do differently
- Count results with a script instead of by eye.
- Change one variable at a time, so I can tell which change moved the result.
- Run enough games to tell a real effect from chance.
- Python
- Ollama (llama3.2)
- requests
- State machines
Other projects
Backend and tools
- zorvyn-finance-engine — A zero-trust ledger API with an immutable ledger, RS256 JWT auth and a test suite (TypeScript, Express, Prisma, PostgreSQL, Redis). Built as an internship assignment.
- git-guard — A GitHub bot that scans pushes for leaked secrets and outdated dependencies, and opens an issue automatically (Python, FastAPI).
- Pythia — A watched-folder tool: rename an empty file to a prompt and Gemini writes the code into it, with RAG over project files (ChromaDB), automatic backups and generated flowcharts (Python).
Apps and team projects
- Bookstore — A full-stack store on Supabase: a Flutter shopping app with catalog, cart and checkout, and a React admin dashboard for product management.
- Path to Balance — A Flutter mental-health screening app with adaptive questionnaires and AI-generated reports, built with a teammate who wrote the Node.js backend.
- SmartDispatch — A team project for a person-to-person delivery platform in Mumbai (Flutter app, Node backend, ML pricing service). I made small contributions.
- interactive_data_sandbox — A Flutter desktop app for exploring machine-learning algorithms on your own data, with a FastAPI and scikit-learn backend.
Games
- Wumpus World — An AI puzzle game built with Flutter and the Flame engine.
- Terminal game in C — A game for the terminal, in progress.
Learning repos: Flutter_Projects, Full-Stack-Web-Development, AIML, AiAgent, edunet-training and a few Flutter starter apps.
I use AI tools as part of my workflow, but I design the systems, review the code and test the results.
Skills
- Mobile
- Flutter
- Dart
- BLoC
- Kotlin (Android accessibility service)
- Web
- React
- Next.js
- TypeScript
- Tailwind CSS
- Node.js
- Express
- Data and backend
- PostgreSQL
- SQLite
- Firebase (Firestore, Auth)
- Supabase
- REST APIs
- JWT auth
- AI / ML
- Python
- scikit-learn
- pandas
- FastAPI
- Local LLMs (Ollama)
- Gemini API
- Tools and testing
- Git and GitHub
- Docker
- Playwright
- Vercel
- Railway
Experience
Software Intern · CRoad Solutions (remote)
Aug 2025 to present
- Built ChatLink, an internal Android app that automates bulk messaging (see below). Developed it alone and have supported it after deployment for about a year.
- Built backend features for an organisation-management platform (Node.js, Express, TypeScript, PostgreSQL, AWS S3 and SQS).
- Contributed to a TypeScript monorepo for a multi-role office platform (React, Express, Drizzle, OpenAPI).
Chemistry teacher, CET and JEE · Ambegaon area, near Pune (part-time)
Second year
- Teaching alongside my mentor of four years at the coaching centre he started. I've learned to run a classroom, plan chapters and manage time while teaching with other teachers.
- It's also what led me to build Chem Arcade.
ChatLink
Internal Android app at CRoad Solutions · built solo · about a year in production
My first officially deployed app, and one people actively use. Before it, the team sent messages by hand to roughly 500 to 800 groups combined. The official business API was too expensive, so I built an alternative: an Android app that reads contacts and groups from an Excel sheet and sends text and media in bulk.
The hard parts
- Finding the UI element IDs. The accessibility service finds each button and field by ID, and locating all the ones it needed took about ten days of trial and error.
- A platform change broke search. The messaging app changed so that only real on-screen taps worked and not sends triggered from other apps. I fixed it by connecting my app to an existing internal Node.js service, built by a colleague, that provides each chat and group's identifier, and then opening each chat directly. I integrated that service and did not write or change it.
- A failed architecture. My first version ran the whole send loop inside the Kotlin side. Messages queued in the background and stacked up, the messaging app kept working even when it wasn't open, and after a while it skipped targets or crashed. I redesigned it as an orchestrator and worker: Flutter decides which message goes to which group, one at a time, and Kotlin opens the chat, sends, and hands control back before the next one starts. That removed the stacking, the skips and the crashes.
Reliability and upkeep
- If a send fails, the app skips it, logs it and carries on. Users can export the log as a report.
- Because the app depends on another app's UI, changes on their side can break it. After a year of fixes it needs as little hand-holding as this approach allows.
Load the Excel sheetFlutter app
Targets and the message
Look up chat and group IDsNode.js service
From an existing internal service
Pick the next target and send one commandFlutter (orchestrator)
BLoC state, local storage in sqflite
Open the chat, send, report backKotlin (worker)
Then hand control back
Record the resultFlutter
On failure: skip, log, continue
Export a reportUser
- Flutter
- Kotlin
- BLoC
- sqflite
- Android accessibility service
- Excel parsing
Education: BE in Computer Engineering, graduated May 2026
Contact
The quickest way to reach me is email. If you'd rather talk first, book a short intro call.
hello@ameypacharkar.com · GitHub