About DevPet
The Inspiration
DevPet was born from a simple observation: coding alone, for hours on end, gets lonely.
I loved the idea of Tamagotchi as a kid; that little digital companion that lived on your keychain and reacted to how you treated it. I wanted that same feeling for developers. A pixel pal that sits on your desktop, watches you code, and makes those long solo sessions feel a little less isolated.
I was also frustrated by the lack of lightweight productivity tools. Most time-trackers are heavy, bloated, and distract from the actual coding. I wanted something that:
- Stays out of your way (transparent, always-on-top, click-through)
- React to what you do, not just track time
- Helps you stay healthy (hydration, eye breaks, posture)
- Celebrates your wins (achievements, streaks, personal bests)
How I Built It
Architecture Decision
I chose Tauri 2.0 over Electron for one reason: Rust.
Electron bundles an entire Chromium instance (~100MB+). Tauri uses the system's native WebView, keeping the bundle tiny. The Rust backend handles:
- Native window management (transparent, always-on-top, ghost mode)
- File system watching (detecting your code changes in real-time)
- Active window detection (knowing when you're coding vs. slacking)
The frontend is Vanilla JavaScript with HTML5 Canvas; no frameworks, no dependencies. Just pure, fast DOM manipulation and pixel-perfect rendering.
The Momentum Algorithm
One of the core features is the Momentum Tracker, which maps your coding activity to 5 levels: cold → warming → flowing → hot → fire.
The algorithm uses an exponential decay model:
$$ M(t) = M_0 \cdot e^{-\lambda t} + \sum_{i=1}^{n} w_i \cdot e^{-\lambda (t - t_i)} $$
Where:
- $M(t)$ is the momentum at time $t$
- $\lambda$ is the decay constant (how quickly momentum fades when you stop coding)
- $w_i$ is the weight of each file change event
- $t_i$ is the timestamp of event $i$
In practice, this means:
- Rapid edits → momentum spikes fast
- Slow, thoughtful changes → steady growth
- Going AFK → momentum decays gracefully (no harsh cutoffs)
Pixel Art & Animation
The character is rendered as a 32×32 pixel spritesheet at 4× scale (128×128 on screen). Each of the 16 animation states has its own row in the spritesheet:
| State | Row | Frames | Duration Array |
|---|---|---|---|
| idle | 0 | 3 | [200, 300, 250] |
| coding | 1 | 4 | [150, 200, 150, 200] |
| thinking | 2 | 3 | [300, 400, 350] |
...
The spritesheet supports runtime palette swapping; skin tones are applied pixel-by-pixel via Canvas API, meaning you can have 12 unique skins with different color schemes without storing 12 separate images.
Procedural Audio
One of my favorite challenges: zero sound files. Every sound is synthesized in real-time using the Web Audio API:
- 9 voice presets (mumble, squeaky, gruff, alien, robot, mystic, hyper, retro)
- Each controls: pitch, waveform mix, syllable timing, formants, vibrato
- Speech-like sounds generated via frequency modulation
The alien voice, for example, uses: $$ f(t) = A \cdot \sin(2\pi f_c t + I \cdot \sin(2\pi f_m t)) $$ Where $f_c$ is the carrier frequency, $f_m$ is the modulation frequency, and $I$ is the modulation index.
What I Learned
- Event-Driven Architecture is Powerful
All 80+ modules communicate through a central EventBus (pub/sub pattern). No module directly calls another. This means:
- Decoupling: The ActivityMonitor doesn't need to know about AchievementSystem
- Scalability: Adding a new feature is just: subscribe to events, emit your own
- Debuggability: I can intercept any event and log it
- Rust + JavaScript is a Great Combo
Tauri's #[tauri::command] macro makes Rust functions available in JS with zero boilerplate:
#[tauri::command] fn scan_project_markers(dir_path: String) -> Result { // Rust backend doing heavy lifting }
const result = await invoke('scan_project_markers', { dirPath: '/home/sidd/myproject' });
- Pixel Art is Hard (But Rewarding)
Designing 16 animation states × up to 4 frames each = 64 unique frames. Each frame is 32×32 = 1,024 pixels to hand-tune. The sprite editor I built inside DevPet helps, but pixel art takes patience.
- Desktop Apps Have Unique Challenges
- Cross-platform window management: macOS, Windows, and Linux all handle "always-on-top" and "transparent windows" differently
- File system watching: notify-rs handles most cases, but network drives and WSL paths need special handling
- Binary size matters: Tauri's ~8MB bundle vs. Electron's ~100MB+ makes a huge difference for downloads
Challenges I Faced
- The "Stuck Detector" Heuristic
Detecting when a developer is "spinning their wheels" (repeatedly editing the same file without progress) required tuning:
$$ \text{Stuck Score} = \frac{\text{Edits on same file}}{\text{Time window}} \times \text{Frustration multiplier} $$
Too sensitive → false positives. Too lenient → misses real stuck states. I ended up using a rolling window of 10 minutes with a threshold of 3+ edits to the same file.
- Claude Code Integration
Hooking into Claude Code's lifecycle (SessionStart, PostToolUse, SessionEnd) meant understanding:
- How Claude Code's hook system works (~/.claude/settings.json)
- Event timing (PostToolUse fires after every tool call)
- State reconstruction (reading JSONL event logs to rebuild session context)
The result: DevPet can tell when you're in an AI pair programming session vs. coding solo — and celebrates when you and Claude ship something together.
- Cross-Platform Builds
Each platform has quirks:
- macOS: Code signing, notarization, macOS-private-api for transparent windows
- Windows: AppUserModelID for notification branding, Git Bash path detection
- Linux: Wayland vs. X11 window management differences
- The Sprite Editor
Building a pixel art editor inside the app itself was a meta-challenge:
- Frame navigation (next/prev with keyboard)
- Color palette with live preview
- Animation playback with FPS control
- Minimap of the entire spritesheet
The trickiest part: canvas-to-canvas pixel copying with palette swapping in real-time.
What's Next?
DevPet is live at github.com/okottorika/DevPet. I'm working on:
- More skins (community contributions welcome!)
- Plugin system (let users write their own reaction handlers)
- Discord Rich Presence (show your momentum level in your status)
- Mobile companion app (check your coding streak from your phone)
Built With
- html5
- javascript
- next.js
- python
- rust
- tauri
Log in or sign up for Devpost to join the conversation.