How Sniffnet Renders and Updates Its Real-Time Traffic Chart: A Deep Dive

Sniffnet renders its real-time traffic chart using the plotters-iced2 crate, where the TrafficChart struct stores spline-based time series data and implements the Chart trait to draw inbound/outbound area series that update automatically when the Sniffer receives new InfoTraffic messages.

Sniffnet, the open-source network monitoring application written in Rust, visualizes live packet captures through a smoothly scrolling real-time traffic chart. This component displays incoming and outgoing traffic volumes as colored area charts that refresh continuously during active captures. Understanding the underlying implementation reveals how the project decouples data aggregation from visual rendering while maintaining a responsive 30-second sliding window view.

Data Collection and State Management

The chart’s state lives in src/chart/types/traffic_chart.rs, where the TrafficChart struct maintains four independent time series plus scaling metadata. The struct definition (lines 28-61) appears as follows:

pub struct TrafficChart {
    pub ticks: u32,                     // current time-slot index
    pub out_bytes: ChartSeries,
    pub in_bytes: ChartSeries,
    pub out_packets: ChartSeries,
    pub in_packets: ChartSeries,
    pub min_bytes: f32,
    pub max_bytes: f32,
    pub min_packets: f32,
    pub max_packets: f32,
    pub language: Language,
    pub data_repr: DataRepr,            // Bytes / Packets / Bits
    pub style: StyleType,
    pub thumbnail: bool,
    pub is_live_capture: bool,
    pub no_more_packets: bool,
    pub first_packet_timestamp: Timestamp,
}

When the capture engine processes a new batch of packets, the Sniffer::refresh_data method (located in src/gui/sniffer.rs at lines 309-311) forwards an InfoTraffic message to update the chart:

self.traffic_chart.update_charts_data(&msg, no_more_packets);

The update_charts_data method (lines 85-126 in traffic_chart.rs) extracts totals from InfoTraffic.tot_data_info for the current interval. It converts these values to signed floats, storing outbound values as negative numbers so the area appears on the left side of the Y-axis centerline:

let out_bytes_entry = -(info_traffic_msg.tot_data_info.outgoing_data(DataRepr::Bytes) as f32);
let in_bytes_entry  =  info_traffic_msg.tot_data_info.incoming_data(DataRepr::Bytes) as f32;
self.out_bytes.update_series((tot_seconds, out_bytes_entry), self.is_live_capture, no_more_packets);
self.in_bytes .update_series((tot_seconds, in_bytes_entry ), self.is_live_capture, no_more_packets);
self.min_bytes = self.out_bytes.get_min();
self.max_bytes = self.in_bytes.get_max();

When reading from offline pcap files, Sniffer::offline_gap (lines 220-224 in sniffer.rs) detects temporal gaps and calls push_offline_gap_to_splines to insert zero-valued points, ensuring the chart displays flat lines during capture pauses rather than interpolating across the gap.

Rendering with Plotters

TrafficChart implements the Chart<Message> trait from plotters-iced2, with the core rendering logic residing in the build_chart method (lines 152-207 in traffic_chart.rs). This method constructs a Cartesian coordinate system and draws the visualization in distinct stages:

  1. Configure drawing area: set_margins_and_label_areas removes margins in thumbnail mode to maximize drawing space.
  2. Compute axis ranges: x_axis_range() returns the last 30 seconds (or a single-point placeholder), while y_axis_range() uses the pre-calculated min/max values from the series (multiplying by 8 when displaying bits).
  3. Draw mesh: Configures axis styles, grid lines, and label formatters for both axes.
  4. Render area series: Calls area_series(direction) to build AreaSeries objects from the spline data, applying the current style’s color and opacity (alpha_chart_badge).
  5. Draw zero line: A horizontal LineSeries at y = 0 masks the negative outbound values during live captures.
  6. Configure legend: Positions the legend in the upper-right corner using localized labels from incoming_translation and outgoing_translation.

The resulting chart displays two filled areas—typically green for incoming and orange for outgoing traffic—that smoothly interpolate between data points using the internal spline representation.

GUI Integration and Update Cycle

The chart integrates into the Iced-based GUI through the view method, which returns an Element containing a ChartWidget:

pub fn view(&self) -> Element<'_, Message, StyleType> {
    let x_labels = if self.is_live_capture || self.thumbnail { None } else { /* timestamps */ };
    Column::new()
        .push(ChartWidget::new(self))
        .push(x_labels)
        .into()
}

This view is embedded in two distinct locations:

The update cycle follows a reactive pattern: when the backend thread sends a BackendTrafficMessage::TickRun, the Sniffer::tick_run method triggers refresh_data, which mutates the TrafficChart state. Because Iced’s architecture automatically redraws widgets when their underlying data changes, the chart updates fluidly without explicit frame scheduling, creating the illusion of a continuously scrolling real-time graph.

Summary

  • State storage: TrafficChart maintains four ChartSeries instances (incoming/outgoing bytes/packets) with min/max bounds for dynamic Y-axis scaling in src/chart/types/traffic_chart.rs.
  • Data flow: Raw packets transform into InfoTraffic messages, which Sniffer::refresh_data (in src/gui/sniffer.rs) passes to update_charts_data to populate the series.
  • Rendering engine: The Chart trait implementation in TrafficChart leverages plotters-iced2 to build Cartesian coordinates, draw smoothed area series, and configure legends via build_chart.
  • UI integration: The view method produces a ChartWidget embedded in both the main overview and thumbnail pages, with layout adjustments controlled by the thumbnail boolean flag.
  • Real-time updates: Iced’s reactive framework automatically re-renders the widget whenever the underlying data changes, driven by tick messages from the capture backend.

Frequently Asked Questions

What charting library does Sniffnet use for its real-time visualization?

Sniffnet uses plotters-iced2, an Iced-compatible backend for the Plotters Rust visualization library. The TrafficChart struct implements the Chart<Message> trait from this crate, utilizing its ChartBuilder API to construct Cartesian coordinates and draw area series with anti-aliased splines.

How does Sniffnet distinguish between incoming and outgoing traffic on the same chart?

The application stores outgoing traffic values as negative floats while keeping incoming values positive. During rendering in build_chart, a horizontal zero-line hides the negative portion's edge, causing the outbound area to extend leftward from the centerline and the inbound area to extend rightward, creating a symmetrical visualization around the Y-axis origin.

Why does the chart show flat lines instead of drops when reading offline pcap files?

When parsing pcap files, Sniffer::offline_gap detects temporal gaps between packets and calls push_offline_gap_to_splines to insert zero-valued data points into the ChartSeries. This ensures the spline interpolation creates a flat line at zero during inactive periods rather than drawing connecting lines across the time gap, accurately representing the absence of traffic.

How does Sniffnet handle different data representations (Bytes, Packets, Bits)?

The TrafficChart stores separate ChartSeries for both bytes and packets simultaneously. When a user switches the display mode via the UI dropdown (triggering Sniffer::data_repr_selection), the chart simply changes which series it queries during the view and build_chart phases. For Bits mode, the Y-axis range calculation multiplies the byte values by 8 before determining scale limits.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →