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:
- Configure drawing area:
set_margins_and_label_areasremoves margins in thumbnail mode to maximize drawing space. - Compute axis ranges:
x_axis_range()returns the last 30 seconds (or a single-point placeholder), whiley_axis_range()uses the pre-calculated min/max values from the series (multiplying by 8 when displaying bits). - Draw mesh: Configures axis styles, grid lines, and label formatters for both axes.
- Render area series: Calls
area_series(direction)to buildAreaSeriesobjects from the spline data, applying the current style’s color and opacity (alpha_chart_badge). - Draw zero line: A horizontal
LineSeriesat y = 0 masks the negative outbound values during live captures. - Configure legend: Positions the legend in the upper-right corner using localized labels from
incoming_translationandoutgoing_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:
- Overview page:
container_chartinsrc/gui/pages/overview_page.rs(lines 92-106) displays the full-size chart with titles and controls. - Thumbnail page:
src/gui/pages/thumbnail_page.rs(lines 53-59) embeds a compact version wherethumbnail = truetriggers reduced margins and hidden X-axis labels.
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:
TrafficChartmaintains fourChartSeriesinstances (incoming/outgoing bytes/packets) with min/max bounds for dynamic Y-axis scaling insrc/chart/types/traffic_chart.rs. - Data flow: Raw packets transform into
InfoTrafficmessages, whichSniffer::refresh_data(insrc/gui/sniffer.rs) passes toupdate_charts_datato populate the series. - Rendering engine: The
Charttrait implementation inTrafficChartleveragesplotters-iced2to build Cartesian coordinates, draw smoothed area series, and configure legends viabuild_chart. - UI integration: The
viewmethod produces aChartWidgetembedded in both the main overview and thumbnail pages, with layout adjustments controlled by thethumbnailboolean 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →