Spotify Pomodoro
- Event
- Open Source
- Overview
A self-hosted pomodoro timer with Spotify integration and overtime mode that respects your flow state. Built with Effect-TS functional programming throughout, demonstrating production-grade architecture patterns with type-safe services, schema validation, and composable layers.
- Technologies
- Effect-TSAstroSpotify APISQLiteOpen SourceDocker
A focus timer that respects your flow
Most pomodoro timers interrupt you at the worst moment. Spotify Pomodoro features overtime mode that lets you keep working when you're in the zone, tracking extra time without forcing a break.
When your timer hits zero, it keeps counting up (+MM:SS) instead of interrupting. You decide when to take a break.
Full functional programming with type-safe services, schema validation, and composable layers.
Your productivity data stays on your machine. SQLite database, Docker deployment, optional auth.
Code Highlights
Clean, type-safe implementations that prioritize developer experience and maintainability.
Computed properties that handle the transition from countdown to count-up seamlessly.
get isOvertime(): boolean {
return this.remainingSeconds <= 0
&& this.status === "running";
}
get displayTime(): string {
const totalSeconds = Math.abs(this.remainingSeconds);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
const sign = this.isOvertime ? "+" : "";
return `${sign}${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
}Dependency injection with compile-time tracking. Services expose streams for reactive state updates.
export class Timer extends Effect.Service<Timer>()("Timer", {
effect: Effect.gen(function* () {
const stateRef = yield* SubscriptionRef.make(initialTimerState);
const start = Effect.gen(function* () {
// Pure functional timer logic with Effect streams
});
return {
start,
pause,
reset,
changes: stateRef.changes
};
}),
accessors: true,
}) {}Dynamic two-note chime generated at runtime. More pleasant than static beep files.
const playChime = Effect.sync(() => {
const ctx = new AudioContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain).connect(ctx.destination);
osc.frequency.setValueAtTime(880, ctx.currentTime);
osc.frequency.setValueAtTime(660, ctx.currentTime + 0.15);
gain.gain.linearRampToValueAtTime(0.9, ctx.currentTime + 0.02);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.5);
osc.start();
osc.stop(ctx.currentTime + 0.5);
});Effect Services Architecture
The application uses Effect-TS's service pattern for clean dependency injection. Each service encapsulates a specific concern and can be provided with mock implementations during testing.
Layer Composition
React Components
App, TimerDisplay, StatsDialog, PlaylistSelector
Effect Runtime
Browser singleton with useTimer hook bridge
Service Layer
Timer, SpotifyAuth, SpotifyClient, Audio, SessionRepo
Astro API Routes
REST endpoints for session persistence
SQLite + Drizzle
pomodoros, focusSessions, breakSessions
Effect Services
Timer
Core timer logic with overtime
SpotifyAuth
OAuth with PKCE flow
SpotifyClient
Playback control API
Audio
Web Audio synthesis
SessionRepo
Database operations
Why Effect-TS? Compile-time dependency tracking, type-safe error handling with discriminated unions, and composable layers that make testing straightforward. Each service declares its dependencies explicitly.
Features & Controls
Every action is accessible without reaching for the mouse. Perfect for developers who prefer keyboard navigation.
Comprehensive analytics computed from raw session data. No pre-aggregation needed—all metrics calculated on demand.
Streaks
Current and longest streak tracking across consecutive days
Time Windows
Stats filtered by today, week, month, and year
Overtime Tracking
See how much extra time you worked beyond configured limits
Tech Stack
Frontend
Backend
Infrastructure
Ready to transform your engineering?
Whether you need technical leadership, enterprise development, or team optimization—let's discuss how we can help.