GnuplotEx.LiveView.Streaming (gnuplot_ex v0.5.0)

Streaming helpers for real-time plot updates in LiveView.

Provides utilities for:

Async Rendering

For plots that take time to render, use async rendering to avoid blocking the socket:

def mount(_params, _session, socket) do
  socket =
    socket
    |> assign(:plot, build_plot())
    |> prepare_async(:plot, :plot_result)

  {:ok, socket}
end

def render(assigns) do
  ~H"""
  <.live_gnuplot_async result={@plot_result}>
    <:loading><.spinner /></:loading>
  </.live_gnuplot_async>
  """
end

Debounced Streams

For high-frequency updates (e.g., sensor data), use debounced streams to limit render frequency:

def mount(_params, _session, socket) do
  socket =
    socket
    |> new_stream(:sensor_plots, debounce: 100)

  {:ok, socket}
end

def handle_info({:sensor_data, data}, socket) do
  plot = GnuplotEx.scatter(data)
  {:noreply, push_stream(socket, :sensor_plots, "sensor-1", plot)}
end

Summary

Functions

Render a plot and broadcast to channel subscribers.

Handle the debounce timer message.

Create a new debounced stream for plot updates.

Prepare an async rendering task for a plot.

Push a plot update to a stream with debouncing.

Functions

broadcast_plot(topic, plot_id, plot, format, opts \\ [])

@spec broadcast_plot(String.t(), String.t(), GnuplotEx.Plot.t(), atom(), keyword()) ::
  :ok | {:error, term()}

Render a plot and broadcast to channel subscribers.

Combines rendering with channel broadcasting for real-time updates.

Example

# In a GenServer or process monitoring data:
def handle_info({:data, data}, state) do
  plot = GnuplotEx.scatter(data)
  :ok = broadcast_plot("dashboard", "sensor-1", plot, :svg)
  {:noreply, state}
end

handle_debounce(socket, stream_name, plot_id)

Handle the debounce timer message.

Add this to your handle_info/2:

def handle_info({:gnuplot_debounce, stream_name, plot_id}, socket) do
  {:noreply, GnuplotEx.LiveView.Streaming.handle_debounce(socket, stream_name, plot_id)}
end

new_stream(socket, stream_name, opts \\ [])

Create a new debounced stream for plot updates.

Streams batch rapid updates to reduce render frequency while keeping the UI responsive.

Options

  • :debounce - Minimum ms between updates (default: 50)
  • :format - Output format (default: :svg)

Example

socket
|> new_stream(:charts, debounce: 100)

prepare_async(socket, plot_key, result_key, opts \\ [])

Prepare an async rendering task for a plot.

This sets up a LiveView async assign that renders the plot in a background task, allowing the socket to continue processing.

Parameters

  • socket - The LiveView socket
  • plot_key - The key where the plot is stored in assigns
  • result_key - The key to store the async result
  • opts - Options

Options

  • :format - Output format (default: :svg)
  • :width - Plot width (default: 800)
  • :height - Plot height (default: 600)

Example

socket
|> assign(:plot, my_plot)
|> prepare_async(:plot, :plot_result)

push_stream(socket, stream_name, plot_id, plot)

Push a plot update to a stream with debouncing.

If updates arrive faster than the debounce interval, only the latest plot will be rendered. This prevents overwhelming the renderer during rapid data updates.

Example

# In handle_info
def handle_info({:data, data}, socket) do
  plot = GnuplotEx.scatter(data)
  {:noreply, push_stream(socket, :charts, "my-chart", plot)}
end