Charts for live view

Hey guys!

I want to create a toy project that shows a chart of temperature over time and updates every 5 seconds. I feel LiveView is perfect for that use case because there will always be only one piece of data appended on the right side of the chart, and one piece of data deleted on the left.

However, most JS libraries I tried don’t do it this way. They need to take all data and call chart.update rerendering the entire chart. I also need to bypass LiveView with a channel to call that update function.

I wondered if you know any solutions that could use the power of LiveView in full :slight_smile:
Maybe you had experience with charts using SVG?
Or had a chart with only HTML divs and CSS?

2 Likes

You can do

<div data-chart="<%= Jason.encode(@data) %>" phx-update="ignore" phx-hook="MyChart">
  <%# mount js chart inside here %>
</div>

The js hook will be called each time when data-chart is updated and phx-update="ignore" does not prevent the attribute from being updates, but probably only it’s children. So you can use the update callback of the js hook to update the chart with new data.

I’d strongly recommend to stick to svg for graphics. I’ve done some experiments with svgs in liveviews and it works quite well if you know how to handle svg. Only downside is that charts most often aren’t just the svg part, but also quite a lot of interaction like labels, tooltips and such things, which are imho not really a good fit for being handled by liveview, so a proper charting library might still be a good idea.

If you still want to go the route of creating the svg/interaction manually: I’ve tried out the technique of the start of this post with svelte.js – which works kinda similar to liveview, but on the client side. It allows you to add pure client side interactions, while still easily being able to receive updates to data from the liveview. There are some good examples for building charts with the low level pieces of d3 on the svelte website, but it can also do custom svg content.

13 Likes

Thank you! I wasn’t aware of phx-hook. I was plaing with LiveView before 2.0 and I missed that update.
This will be helpful!

Also, @sasajuric has some bespoke SVG chart rendering in the source code from one of his awesome talks that might be suitable. It’s not the centre piece of the talk. The leex template is here demo_system/example_system/lib/example_system_web/load/graph.html.leex at master · sasa1977/demo_system · GitHub

Finally, if you are happy to homebrew SVG you could take a look at some D3.js samples - I’ve been porting bits and pieces of the scale and axis libraries over to elixir (not too embarrassing but not ready to release yet). If you want to get really deep, search for papers by Hadley Wickham, author of ggplot (an R library providing a nice abstraction for translating data into graphics). I’m trying to build a “charting” library in elixir with ggplot type concepts, learning SVG and layout tricks from D3 (as R code is mighty hard to read with my level of knowledge). It’s happening in fits and starts due to many other commitments. If it ever gets to a reasonable state I’ll let you know - I started on it because I think backend rendered real-time dashboards would be awesome!

8 Likes

Just found chartkick-ex (hex.pm). Have anybody of you tried it with LiveView? I’m thinking to rewrite our client side to LiveView where we have a plenty charts (all realtime measured data)…

2 Likes

SVG charts in Elixir is something I would love to see.

7 Likes

Putting out SVG is pretty much what my PlotEx library linked above does. Apply a few css rules and you can style it however it suits you. Using it with LiveView readily enables live updating! Just plug it in and go.

I’m using a 1 second :tick timer in my LiveView which just takes a new data stream and puts it in an assigns. It’s pretty decent speed for having essentially no optimizations. It runs fine on a raspberry pi embedded, but I haven’t checked the per-user memory usage but I suspect it’s competitive with a JS solution as it doesn’t have to serialize the data and have JS regenerate everything.

I’d like to have time to expand on the api and add more graph types. Elixir Streams make it trivial to say take time series data over days and only show every X’th item. Still if anyone is interested I’d love feedback on how to clean up the API or support more graph types. See the SVG Output module.

3 Likes

How’s this framework going? Is this something that I should include in the Phoenix LiveView book or is there a better alternative?

-bt

1 Like

I have one getting close - missing doco, decent colour palette handling and hex project packaging, but has scatter plot, bar charts (stacked & grouped), basic Gantt chart and reasonable primitives for building new plot types. Not as strong as @elcritch’s PlotEx in terms of performance and liveview bindings (works well in liveview, but less efficient rendering).

It also has a basic event model on the bar chart so you can click on bars and figure out in liveview what’s been clicked.

If anyone is keen to take an early look, I can make time over the next 24hrs to break it out into a new, public github repo.

Edit: @elcritch - more than happy to collaborate on this.

2 Likes

@redrapids – I consider this library stable for live stream plot updating, but it lacks a lot of basic plotting niceties you’d find in a full featured library. Still it works well for it’s intended use case of quickly including live updating graphs for small volume dashboards, especially on embedded devices. My belief is that LiveView will be a goto for making small bespoke dashboard for internal company uses and/or embedded devices. I’m keeping it updated with LiveView as well. I am not aware of anyone else actually using it however.

@mindok – that sounds great! ExPlot is certainly “I have one problem to solve, here it is”. It leaves color palettes and similar up to the user via CSS.

I’d be up for collaborating, though my time is pretty limited to produce much code currently. I’d be interested in checking out your beta. Are you on the Elixir slack channel for DM’ing (I’m @elcritch on both slack and github)? Not sure how you’re handling your data scaling, but PlotEx is broken up into two pieces so in theory you could use the data-processing part for scaling scatter plot / line chart data and build SVG (or even Canvas) portions on top. I tried to pay attention to the use of Streams and striding to ensure that memory usage doesn’t explode. There’s also a fair bit of detail in handling datetime and numeric axis generation in a pleasing manner but that’s all in the “base” api not presentation / SVG side.

I am on slack, but very intermittently - I deliberately haven’t set it up on my main dev laptop. I hear you on the axes / scaling - I read a lot of D3 code - between D3 & ggplot the approach is to make it as nice-looking as possible with minimal effort for the user.

I haven’t paid that much attention to memory usage (other than) so it would be good to hear your approach.

Here’s a couple of screenshots to give you an idea

image

Code for the above

    plot_content = BarPlot.new(dataset, 100, 100, orientation)
      |> BarPlot.defaults()
      |> BarPlot.type(:stacked)
      |> BarPlot.padding(2)
      |> BarPlot.set_val_col_names(Enum.map(1..nseries, fn i -> "Value#{i}" end))

    plot = Plot.new(600, 350, plot_content)
      |> Plot.plot_options(%{legend_setting: :legend_right})
      |> Plot.titles("Title", nil)
      |> Plot.plot_options(%{show_y_axis: true})

    {:safe, Plot.to_svg(plot)}

image

And for the above

    dataset = Reaction.Chart.Dataset.new(data, ["Cat", "Task", "Start", "End"])
    plot_content = GanttChart.new(dataset, 100, 100)
      |> GanttChart.defaults()

    plot = Plot.new(600, 400, plot_content)
      |> Plot.titles("Test Gantt", nil)

    {:safe, Plot.to_svg(plot)}

3 Likes

Thanks to @tangui Plotex v0.3.0 is out that uses SVG Markers for line points. It speed up rendering almost 2x for ~80,000 points! :slight_smile:

2 Likes

Those look great! If you have your code up, I’d like to look into the code.

If anyone is keen to take an early look, I can make time over the next 24hrs to break it out into a new, public github repo.

I’d love to take a look at it.

Did you already tried it out with live-view?

yes please!

Done…

6 Likes

Sorry for late answer. I overlooked your question. I haven’t tried yet :frowning:

Sorry to revive an old thread, but I think this may be useful for others. I was looking at doing charts in LiveView this past week. I really wanted charts to be rendered with JS rather than SVG, due to the interactive element.

I found the Chartkick library, but it didn’t work with LiveView (at least for me) due to how it expects script tags to run. I was able to pretty quickly modify chartkick.ex to work with LiveView though.

I was unsure how to get rid of the raw requirement with Chartkick.ex, but tbh I didn’t spend more than a few minutes thinking about that.

These charts will update if the data source updates, but does so by destroying and re-allocating the chart. That probably isn’t the most efficient thing, but my use case is static 30-day charts.

1 Like

Hey, did you ever finish porting/releasing this? As a fan of D3.js it would be a very helpful lib to have in Elixir! And it would save me from having to do some annoying mathematical calculations (this is the real reason I’m asking :joy:)