Back to Portfolio
Open SourceTechnologyDec 12, 2025

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.

Overtime Mode

When your timer hits zero, it keeps counting up (+MM:SS) instead of interrupting. You decide when to take a break.

Effect-TS Architecture

Full functional programming with type-safe services, schema validation, and composable layers.

Self-Hosted First

Your productivity data stays on your machine. SQLite database, Docker deployment, optional auth.

Implementation

Code Highlights

Clean, type-safe implementations that prioritize developer experience and maintainability.

Overtime Mode

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")}`;
}
SchemaComputed PropsUX
Effect Service

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,
}) {}
Effect.ServiceSubscriptionRefStreams
Web Audio Synthesis

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);
});
Web Audio APIOscillatorEnvelope
System Design

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.

User Experience

Features & Controls

Keyboard-First Controls

Every action is accessible without reaching for the mouse. Perfect for developers who prefer keyboard navigation.

Space / EnterStart timer
BSkip to break (during focus)
FSkip to focus (during break)
EEnd session early
RReset timer
Rich Statistics

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

Technology

Tech Stack

Frontend

Astro 5SSR with React islands
React 19Interactive components
Tailwind CSS v4Utility-first styling
shadcn/uiRadix-based components

Backend

BunFast JS runtime
Effect-TS 3.xFunctional programming
SQLiteEmbedded database
Drizzle ORMType-safe queries

Infrastructure

DockerContainerization
CoolifySelf-hosted deployment
VitestUnit testing
BiomeLinting & formatting

Ready to transform your engineering?

Whether you need technical leadership, enterprise development, or team optimization—let's discuss how we can help.