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
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]