# How Frigate's Timeline and Event System Works: A Technical Deep Dive

> Explore Frigate's timeline and event system. This technical deep dive reveals the Python thread, Peewee database, and TypeScript utilities behind Frigate's object, audio, and API event recording.

- Repository: [Blake Blackshear/frigate](https://github.com/blakeblackshear/frigate)
- Tags: deep-dive
- Published: 2026-05-25

---

**Frigate records every detection—objects, audio, and API-generated events—in a chronological timeline using a three-part architecture consisting of a Python processing thread, a Peewee database model, and TypeScript frontend utilities.**

The **Frigate timeline and event system** provides a complete audit trail of all detection activity in the blakeblackshear/frigate repository. This system captures object movement, zone entries, stationary states, and audio detections, storing them in a queryable format for the web interface and external analytics.

## Core Components of the Frigate Timeline Architecture

### TimelineProcessor: The Backend Event Consumer

The `TimelineProcessor` class in [[`frigate/timeline.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/timeline.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/timeline.py) runs as a dedicated thread that consumes a multiprocessing queue (`timeline_queue`). Detection pipelines populate this queue with events that the processor transforms into structured timeline entries.

The processor extracts the **camera name**, **source type**, and **event state** (`start`, `update`, or `end`) from each queue item. It then constructs a base entry containing a timestamp, camera identifier, source ID, and a JSON payload with box coordinates, labels, and confidence scores. Depending on the event state, it creates specialized entries with `class_type` values such as `visible`, `entered_zone`, `stationary`, `active`, `attribute`, or `gone`.

To prevent data loss, the processor maintains a `pre_event_cache` that holds timeline entries until their associated event is persisted in the database. This ensures that timeline rows are never orphaned if an event is still being processed.

### Timeline Database Model: Flexible SQL Storage

The [`Timeline`](https://github.com/blakeblackshear/frigate/blob/dev/frigate/models.py) model in [`frigate/models.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/models.py) defines a lightweight Peewee ORM table with the following schema:

- `timestamp`: When the event occurred
- `camera`: The camera identifier (e.g., "front_door")
- `source`: The detection origin ("tracked_object", "audio", or "api")
- `source_id`: Unique identifier for correlation
- `class_type`: Semantic meaning of the entry (event state)
- `data`: JSON field containing bounding boxes, scores, zones, and attributes

This schema supports time-based queries while keeping the payload flexible through the JSON column, allowing new detection attributes to be added without schema migrations.

### Frontend Utilities: Time-Range Chunking

The TypeScript utilities in [[`web/src/utils/timelineUtil.tsx`](https://github.com/blakeblackshear/frigate/blob/main/web/src/utils/timelineUtil.tsx)](https://github.com/blakeblackshear/frigate/blob/dev/web/src/utils/timelineUtil.tsx) handle the display layer. The `getChunkedTimeDay` function slices a user-requested time interval into hourly `TimeRange` chunks, handling partial hours at the end of ranges. The `findChunkIndex` function locates which chunk contains a specific Unix timestamp using half-open intervals, enabling efficient pagination and lazy loading of timeline data in the UI.

## How Timeline Entries Are Created and Processed

### Processing Detection Events

The `TimelineProcessor` distinguishes between different event sources through the `handle_api_entry` method, which processes audio detections and external API events separately from tracked objects. API-generated events create "heard" or "external" entries that are stored immediately without caching, while object tracking events use the pre-event cache mechanism.

Each queue entry processed by the `run()` method follows this structure:

```python
from multiprocessing import Queue, Event as MpEvent
from frigate.timeline import TimelineProcessor
from frigate.config import FrigateConfig

# Initialize the queue and stop event

timeline_q = Queue()
stop_evt = MpEvent()

# Start the processor thread

processor = TimelineProcessor(config, timeline_q, stop_evt)
processor.start()

# Queue a detection event

timeline_q.put((
    "front_door",                     # camera name

    "tracked_object",                 # source type

    "start",                          # event state

    None,                             # previous data (None for start)

    {
        "id": "obj-123",
        "frame_time": 1685505600.0,
        "box": [0.1, 0.2, 0.3, 0.4],
        "label": "person",
        "score": 0.92,
        "current_zones": [],
        "stationary": False,
        "attributes": {},
        "current_attributes": [],
    },
))

```

### API-Generated Event Handling

Audio detections and external events bypass the standard object tracking pipeline. When the processor receives these via `handle_api_entry`, it creates entries with source types "audio" or "api" and class types like "heard", storing them directly to the database without the caching logic used for tracked objects.

## Querying and Displaying Timeline Data

### Backend Database Queries

Applications can query the timeline directly using the Peewee model to retrieve events within specific time windows or filter by camera and event type:

```python
from frigate.models import Timeline
from datetime import datetime, timedelta

# Calculate the time cutoff (last hour)

cutoff = datetime.utcnow() - timedelta(hours=1)

# Query timeline entries for a specific camera

entries = (
    Timeline.select()
    .where(
        (Timeline.camera == "front_door") &
        (Timeline.timestamp >= cutoff)
    )
    .order_by(Timeline.timestamp)
)

for entry in entries:
    print(f"{entry.timestamp}: {entry.class_type} - {entry.data}")

```

### Frontend Time Range Processing

The web interface uses chunked time ranges to optimize queries for large historical datasets. The TypeScript utilities split days into hourly segments:

```typescript
import { getChunkedTimeDay, findChunkIndex } from "@/utils/timelineUtil";
import { TimeRange } from "@/types/timeline";

// Define a 9-hour time range
const range: TimeRange = { after: 1685505600, before: 1685538000 };

// Split into hourly chunks
const chunks = getChunkedTimeDay(range);

// Find which chunk contains a specific timestamp
const targetTimestamp = 1685512000;
const chunkIndex = findChunkIndex(chunks, targetTimestamp);

console.log(`Timestamp found in chunk ${chunkIndex}`, chunks[chunkIndex]);

```

The `findChunkIndex` function uses half-open intervals for all chunks except the last, ensuring that boundary timestamps are assigned unambiguously to the correct time segment.

## Summary

- **Frigate's timeline and event system** uses a three-tier architecture: Python processing (`TimelineProcessor`), SQL storage (`Timeline` model), and TypeScript frontend utilities.
- The `TimelineProcessor` consumes a multiprocessing queue, creates typed entries (`visible`, `entered_zone`, `stationary`, etc.), and prevents orphaned records through a `pre_event_cache`.
- Database entries store flexible JSON payloads alongside structured metadata (camera, source, timestamp) for efficient time-based queries.
- Frontend utilities split time ranges into hourly chunks and locate specific timestamps for optimized UI rendering.
- API-generated events (audio/external) are handled separately from tracked objects with immediate persistence.

## Frequently Asked Questions

### What types of events does Frigate record in the timeline?

Frigate records **tracked object events** (person, vehicle, etc.), **audio detections**, and **external API events**. Object events include states like `entered_zone`, `stationary`, `active`, `visible`, and `gone`. Audio events use the `heard` class type, while API events can have custom classifications depending on the source.

### How does Frigate handle pre-event data to avoid orphaned records?

The `TimelineProcessor` maintains an in-memory `pre_event_cache` that stores timeline entries for events that haven't been fully persisted yet. Once the associated event is confirmed and saved to the database, the cached timeline entries are flushed to the `Timeline` table. This prevents timeline rows from referencing non-existent events if the system restarts or encounters errors during detection processing.

### What is the database schema for the Timeline table?

The `Timeline` table uses a Peewee ORM model with columns for `timestamp` (datetime), `camera` (string), `source` (enum: tracked_object/audio/api), `source_id` (string), `class_type` (string describing the event state), and `data` (JSON). The JSON field contains detection-specific details like bounding box coordinates, confidence scores, zone lists, and attribute dictionaries.

### How does the frontend optimize timeline queries for large time ranges?

The frontend uses the `getChunkedTimeDay` utility to split large time ranges into hourly chunks. This allows the UI to implement pagination and lazy loading, fetching only the data for the currently viewed hour rather than loading entire days at once. The `findChunkIndex` function quickly locates which chunk contains a specific timestamp, enabling efficient navigation to specific moments in the timeline view.