Quali Duel

F1 Qualifying Lap Comparison - Architecture Overview

System Architecture

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#6ec8ff', 'primaryTextColor': '#d7dee7', 'primaryBorderColor': '#2a3442', 'lineColor': '#6ec8ff', 'secondaryColor': '#121821', 'tertiaryColor': '#0b0f14' }}}%% flowchart TB subgraph Client["Browser Client"] UI[Quali Duel UI] Charts[Telemetry Charts] Map[Track Progress Map] end subgraph NextJS["Next.js App"] Pages[Page Components] API[API Route Handlers] Cache[Response Cache] end subgraph DataLayer["Data Access Layer"] OpenF1Client[OpenF1 Client] Mappers[Data Mappers] Telemetry[Telemetry Pipeline] end subgraph External["External APIs"] OpenF1[(OpenF1 API)] FastF1[(FastF1 - Future)] end UI --> Pages Charts --> Pages Map --> Pages Pages --> API API --> Cache Cache --> OpenF1Client OpenF1Client --> Mappers Mappers --> Telemetry OpenF1Client --> OpenF1 Telemetry -.-> FastF1
Primary Data Flow
Future Integration

Data Flow Pipeline

%%{init: {'theme': 'dark'}}%% flowchart LR A[User Selects\nSession + Laps] --> B[Fetch\nMetadata] B --> C[Fetch\ncar_data] B --> D[Fetch\nlocation] C --> E[Merge\nSamples] D --> E E --> F[Normalize\nLap Progress] F --> G[Build\nComparison\nPayload] G --> H[Render\nCharts]

UI Component Tree

%%{init: {'theme': 'dark'}}%% flowchart TD Shell[ComparisonPageShell] --> Selectors[SessionAndLapSelectors] Shell --> Cards[LapSummaryCards] Shell --> Delta[DeltaTraceChart] Shell --> Stack[TelemetryStackCharts] Shell --> Track[TrackProgressMap] Stack --> Speed[Speed Trace] Stack --> Throttle[Throttle Trace] Stack --> Brake[Brake Trace] Stack --> Gear[Gear Trace]

Telemetry Processing Sequence

%%{init: {'theme': 'dark'}}%% sequenceDiagram participant U as User participant UI as Quali Duel UI participant API as API Routes participant OC as OpenF1 Client participant TP as Telemetry Pipeline participant OF as OpenF1 API U->>UI: Select Session UI->>API: GET /api/sessions API->>OC: fetchSessions() OC->>OF: /sessions?type=Qualifying OF-->>OC: Session List OC-->>API: Mapped Sessions API-->>UI: Session Options U->>UI: Select Two Laps UI->>API: GET /api/lap-comparison API->>OC: fetchLapData(lap1, lap2) OC->>OF: /car_data + /location OF-->>OC: Raw Telemetry OC-->>TP: Merged Samples TP->>TP: Normalize Progress TP->>TP: Calculate Delta TP-->>API: LapComparisonPayload API-->>UI: Comparison Data UI->>UI: Render Charts

Module Structure

%%{init: {'theme': 'dark'}}%% classDiagram class OpenF1Client { +fetchSessions() +fetchDrivers() +fetchLaps() +fetchCarData() +fetchLocation() } class TelemetryPipeline { +mergeLapSamples() +normalizeLapProgress() +buildComparisonPayload() } class LapComparisonPayload { +lapA: LapData +lapB: LapData +delta: DeltaTrace +gainLossZones: Zone[] +metadata: ComparisonMeta } class ChartComponents { +DeltaTraceChart +SpeedChart +ThrottleBrakeChart +GearChart } OpenF1Client --> TelemetryPipeline TelemetryPipeline --> LapComparisonPayload LapComparisonPayload --> ChartComponents

State Management

%%{init: {'theme': 'dark'}}%% stateDiagram-v2 [*] --> Empty: Page Load Empty --> SessionSelected: Select Session SessionSelected --> DriversLoaded: Fetch Drivers DriversLoaded --> LapsSelected: Select 2 Laps LapsSelected --> Loading: Fetch Comparison Loading --> ComparisonReady: Data Loaded Loading --> Error: Fetch Failed ComparisonReady --> LapsSelected: Change Laps ComparisonReady --> SessionSelected: Change Session Error --> LapsSelected: Retry note right of ComparisonReady URL params synced for shareability end note

Development Phases

Phase 1
Research & Scope
1 day
Phase 2
Data Access Layer
2 days
Phase 3
Lap Comparison UX
3 days
Phase 4
Test & Polish
1 day

Tech Stack

TS
Next.js + TypeScript - Full-stack app with API routes
OF
OpenF1 API - Primary data source for qualifying telemetry
RC
Recharts / visx - Telemetry visualization charts
FM
Framer Motion - UI transitions and chart animations

Gain/Loss Visualization Concept

%%{init: {'theme': 'dark'}}%% xychart-beta title "Delta Time Over Lap Progress" x-axis "Lap Progress %" [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] y-axis "Delta (seconds)" -0.5 --> 0.5 line [0, 0.05, 0.12, 0.08, -0.02, -0.15, -0.22, -0.18, -0.25, -0.30, -0.32]
Driver A Ahead
Driver B Ahead