Hacker News new | past | comments | ask | show | jobs | submit login
Easy SVG sparklines (alexplescan.com)
353 points by alexpls on July 10, 2023 | hide | past | favorite | 103 comments



Maybe it's just because I have so much experience with design and visual art, but I think SVG is one of the most, if not the most underutilized web format. It's great for the self-contained static vector graphics that it's most commonly used for, but it can do so much more. SMIL animations can be a little clunky, but having an alternative to gif and video that doesn't require JS is pretty rad-- especially for throbbers and things like that. That you can work with SVGs so easily using JS and CSS is awesome. You can even build your own filter stacks using its built-in effects... though last time I did that with a detailed full-screen art piece, performance was rough.


>SVG is one of the most, if not the most underutilized web format

Yes. I think the problem is that you have to learn to author with it before you program with it, and the learning curve is actually fairly steep. OTOH the feedback is immediate and satisfying.

Personally, I've ignored SVG's built-in animation capabilities in favor of pumping DOM modifications into the scenegraph with a (requestAnimationFrame) timer. This gives you exquisite control requiring very little code.

https://simpatico.io/svg.md#clocksvg


+1 for requestAnimationFrame. SMIL animations are cool until you try doing 1,000 of them. Still, I do agree that SVG is underutilized. Not only can it do so much more but it has effects that can be used in conjunction with DOM elements. Things like dropshadow, glow, blur, greyscale, duotone, etc. Check out this SVG glitch effect [0]

[0] https://codepen.io/DirkWeber/pen/YzBYWY


Yeah it's great with JS, and I use it for most dynamic things I do with SVG. I'd still stick with SMIL for little animated icons that would be deployed multiple places and other little problems like that.

> Yes. I think the problem is that you have to learn to author with it before you program with it, and the learning curve is actually fairly steep.

Good point.


Totally agree. I bookmarked this guide that hit the top of HN a few weeks back, a good primer on how to get started manually programming paths, which unlocks a lot of cool dynamic SVG and animation opportunities: https://www.nan.fyi/svg-paths.


I try to keep my SVGs simple, and often tweak them by editing the XML data in a text editor. (Linus Torvalds relaxes with assembly language, I make image files smaller...) https://www.phoronix.com/news/Linus-Torvalds-Relax-Inline-AS...

This simple iOS app helps me play with path changes, sort of a unit-test use case for me:

http://genhelp.com/apps/svgpaths.html


I wish <img> would accept `fill` property through css. There's a neat trick to colorize SVG's: https://angel-rs.github.io/css-color-filter-generator/


There's definitely some super clunky stuff, there. "This is an image! no... wait... this is an embedded document! no... hold on... it's an ima... hmm...."

It all makes sense from a technical perspective when you dig into it, but it's totally counterintuitive in surprising ways, sometimes.


Safari renderer is so bad though, every single complex smil animation I’ve ever built just broke into pieces on Safari.


Funny you say this, I spent a good 5+ hours yesterday trying to get an SVG of a UI mockup to render properly on Safari before giving up and implementing it in HTML.

What was especially annoying was that different representations of the exact same SVG would behave differently (inline-svg vs img+src vs object)


SVG has… weirdnesses. I couldn’t say exactly what they are. But it feels like an older format from an XML-centric era. It also feels fairy lax on “optional features” and ambiguous spec (although I’m not sure this is true). There are noticeable differences between different rendering engines.

Most of drawing works fine as markup. However, animations and simple state changes are awful. CSS is much easier to use for some of those purposes.

I’d very much forgive SVG for its clunkiness, after all it’s old yet very powerful today. It’s a prime candidate for a revamp.


Noticable differences between rendering engines for what? Sometimes type can be janky, but no moreso than in html/css, and in SVG you're generally turning type into vector outlines. For graphics rendering, it's completely accurate in any environment I've used it in, and that's working with very very fussy brands.


I mean I'd say this is pretty noticeable: https://imgur.com/e6TbXFj

Same screen, same zoom levels. Viewbox properly set, height/width set, confirmed compliant units. Switching between img/object would get it unblurry but then lose the embedded image data. Switching to inline-svg would in turn get the image data working but the drop shadow was completely broken.


Yes-- filters and type aren't super consistent. I wouldn't rely on SVG to use either of them for significant elements.


I don’t recall exactly. Filters or blending modes probably.


How was it made?


Figma, and I got to the point of running it through SVGO and hand patching any constructs that had known issues with Safari.

Going based off the types of bugs I was seeing alone I wouldn't trust SVGs for anything but the most basic shapes.


Quickly rifling through a the sites of some huge extremely valuable brands, Apple, Coca Cola, Walmart, Target, and Toyota all use SVG for their logos. SVG is not the right choice to lay out documents. It's not a replacement for PDF. It's a perfect choice for creating discrete vector graphics.


Simple, data-driven graphics like this is one area where SVG really shines, I think. No need to load a JavaScript charting library if you just want some simple line charts like this. I create SVG images fairly often, and maybe half the time I find myself hand-coding them, or at least hand-tweaking them, since I enjoy the magic of seeing code turn into something visual.


SVGs are amazing for interactive visualisation too. Like Flamegraphs: https://www.brendangregg.com/flamegraphs.html


An alternative if you want a bit more help with charting, without client side JS, is to use d3-shape (https://github.com/d3/d3-shape) to server-side render SVGs.


do you have a blog


The "unfinished" tiny line graph with no fill is actually closer to what Tufte meant by sparklines. He meant those to be placed in text, word-sized, along with words to help communicate better.

https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=...

I feel like many people use "sparkline" to mean "pretty graph." Those, frankly, lack the punch of a sparkline.


I like it! But instead of modifying your data to suit SVG's default coordinate system, I suggest using a top-level "g" tag with a "transform/scale" attribute. Like this:

   <svg class="natural-units"
     width="200px" height="200px"
     viewBox="-1 -1 2 2"
   >
     <desc>A static svg unit circle</desc>
     <g transform="scale(1,-1)">
     <circle class='unit-circle' r="1" fill="none" stroke-width=".001 "/>
  </g>
</svg>

Example adapted from: https://simpatico.io/svg.md#naturalunits

In this way you can use the intuition about the coordinate system that you built in school.


Doesn't this flip all text in the SVG upside down?


Alas, yes. And you have to reflip it with another g/scale. But such is life!


A bigger problem is that it breaks text rendering. Transforms turn off pixel snapping in the hinting engine so you’ll get blurry text or lines.

(At least if you applied it as a css transform you would. Maybe if you did it natively in svg you wouldn’t?)


This is cool!

For those who don't know, there's a font called Sparks that uses glyphs to create sparklines: https://github.com/aftertheflood/sparks


all their examples and URLs (save for the code itself on github) but even the font files themselves are dead because that company closed in 2020 :/

Very neat idea, though! I did find this example: https://observablehq.com/@tomgp/sparks-with-live-data

For those examples to work (which they still do), the URL references to the font data in the stylesheet should ostensibly still be valid: https://tools.aftertheflood.com/sparks/styles/font-faces.css


Apparently, the host 'tools.aftertheflood.com' is still mapped to GitHub Pages.

Backing out from the CSS file (many thanks!), turns out the top-level web page is still up -- BUT the links to download the zip file font collections DONT WORK.

So you still need to scrape the CSS file to get working links to the individual fonts... (I was going to post those URLs here, but there are 84 of them. Use a command like this to scrape the CSS file:

   grep -o "http[^']*" < sparks.css
However, this GitHub Pages site still hosts web pages that show some great examples and links to ObservableHQ notebooks (which also still work, hosted at observablehq.com).

https://tools.aftertheflood.com/


oh that's a bummer, didn't know that killed the examples and links to the font files :(

I just created a new issue and uploaded a zip file of their fonts to their Github: https://github.com/aftertheflood/sparks/files/12009401/AtF.S...

(Finally a justification for my font hoarding)


Recent HN thread on another "graphing data" font, although this one doesn't simply map numbers to graphic, prompting a discussion on accessibility issues to consider:

Datalegreya Font https://news.ycombinator.com/item?id=25832196


Why are the compressed fonts _5MB_!?


I've recently used this approach for generating Open Graph images for display when a link to the site is used on Twitter, WhatsApp, Facebook, etc [1]. I was pleasantly surprised at how quickly something could be implemented. The last time I'd done something similar was using Cairo and needing to write more of the scaling dynamics. I don't think I ever got it to adjust to dynamic content very well. This time I put together a prototype in Inkscape, converted it to a template and render it to PNG with Sharp [2].

[1]: https://hntrends.net/api/og?word=twitter [2]: https://github.com/lovell/sharp


But SVG is slower than Canvas. The main use case for sparklines is embedding them into cells, many hundreds or even thousands of them [1]. With hundreds of SVG files page becomes becomes visibly slower (first paint, scroll, interactions). I suggest to invest some time and check canvas solution too.

[1]: https://www.google.com/search?q=sparkline&tbm=isch


If pure client-side performance is key, and you can afford a little extra bandwidth, then go a little further and pre-draw server-side and transfer as date:uri images. Then you are not relying on JS running on the client to draw on the canvases. Not an option if you need things drawn more dynamically client-side, in response to user changes/filters/etc without a server round-trip, of course.

Though in any case if you have thousands of sparklines in cells I'd question if the display is actually useful to anyone.

Unless it is a large table of data you are presenting in which case thousands of rows has display time issues in my experience anyway. I have in mind a CSV preview on one of our support dashboards which takes a noticeable time to render when given a client import of ~8,000 rows and ~15 columns and that is not a lot more than a plain HTML table.


This is only an issue on chrome-based browsers; performance in Firefox is much closer to what you would expect. You can/could (late 2022) reliably crash chrome by displaying 1000+ unique SVG files on one page, with each SVG simply displaying a single line of text. My current workaround is rendering the SVGs to png serverside if the client is chrome-based, as canvas feels like the wrong solution.


Since the vast majority of users are a chromium based browser, doesn't this put the burden on your server pretty much all of the time? why even bother with code to do 2 different things when the other thing is such a niche segment of users?


This kind of thinking is how we ended up with the IE6 disaster.


That's my point of having the logic to do multiple workflows based on browser type.


True; so the server-side rendering is tuned for minimal server load, with the resulting output being heavily degraded. Still good enough for its purpose, and Firefox gets the bonus hi-res thumbnails :-)


Of course SVG is slower than Canvas. SVG is fundamentally much more powerful. Canvas is just a pixel buffer. You know what's even faster than Canvas? JPEG.

Of course these tools have different use cases. Handling scaling events and interactivity with Canvas is far, far more laborious.

The great thing about SVG (and to a lesser extent JPEGs) is that you can produce them anywhere, not just in a browser with a JavaScript VM.


> You know what's even faster than Canvas? JPEG

Is that true? Isn't canvas a bitmap whereas JPEG requires decoding?


What's the fastest way to deliver the bitmap to the canvas?


Hardcode the RGB array?


So, BMP?


I find it hard to imagine a use-case for thousands of sparklines on screen at the same time! And if it’s not on screen, you don’t need to render it.

Instead of using svg files, just include them in the html, and make them simple. Each svg sparkline only needs to be two elements (the <svg> tag and one <path>), which is crazy efficient.

Canvas uses one element, instead of two, but you have to create a custom implementation of path rendering and do all that work in JavaScript instead of native browser APIs.

Canvas pulls ahead with drawing complex images where you have a single pixel buffer representing thousands of individual “shapes” because the DOM itself is optimized for interaction, not just drawing pixels, but I think that’s a different use-case from sparklines.


A spreadsheet with numerous sparklines per row would get you there.


There's an open source project I was briefly involved in called SSVG [1] that renders the SVG as Canvas to speed it up drastically, especially on Chrome. It works as a simple one-line js drop in for many common visualization examples [2].

[1] https://ssvg.io/ [2] https://ssvg.io/examples


In that case, browsers should optimize SVG more.


SVG could never be as fast as canvas.


SVG is a series of drawing commands (with transformations, filters, and so on). Which is exactly what canvas is. With appropriate layer caching of course it can be 100% as fast if the rudiments are similar and in the same context[1], and on many platforms it is. Chromium derivatives have a particularly slow implementation of SVG and it has tainted the whole realm.

[1] Obviously if you're zooming and transforming and animating layers there is going to be a cost, but that should be compared with doing the same with a canvas.


Canvas is just a pixel buffer. Even with sufficient caching SVG would be slower because there’s more work to do with parsing, etc.


Canvas is a pixel buffer...and a set of drawing rudiments to imperatively actually make that pixel buffer useful. If you were actually just using canvas as a pixel buffer it would be catastrophically slow.

SVG is a pixel buffer and a set of drawing rudiments to imperatively or declaratively actually make that pixel buffer useful.

The distinction you are drawing between these is sophistry.


SVG is a pixel buffer the same way html is a pixel buffer. It can have multiple types of animations, hover states, etc.


No the distinction is important and directly related to performance. Supporting the canvas API requires fewer CPU instructions to get to pixels on the screen. The browser has to do a lot more work to turn SVG into pixels


Hard to imagine that pure-HTML SVG is really slower than Canvas which relies on JS..


It is, it is! SVG is DOM, with event handling on every node, with attempts to apply CSS rules. Canvas for non-interactive charts is just "draw once and forget". It is a sequence of moveTo + lineTo, then you have a bitmap and nothing else. Extremely basic graphics, modern JS engines will handle it in the blink of an eye.

I don't even mention the fact that article suggests to return each SVG sparkline in a separate request.


Guess it depends on your definition of performance.


Ah, but Canvas does not rely on JS. You would update it using JS, yes. When you don’t update it, it’s just another image. Browsers are quite good at images.

In the end, I think it’s down to the complexity of the graph and the dimensions (in pixels, because images need memory, too).


How do you draw an image on a canvas without JS?


You don’t. My point is: After drawing, the canvas is “inert”. Rendering to the canvas once is probably more or less as expensive as rendering the SVG once. However, the SVG will probably be rendered a lot more than once. The page developer cannot control it either, the browser decides what’s best.


Canvas is faster _because_ it relies on JS. It’s a much dumber and stateful API - the browser has to do much less work.


Especially if we're talking using "many hundreds or even thousands of them" on a page.


> The main use case for sparklines is embedding them into cells

Says who? Citation?


I ended up hand crafting my svg graphs for non.io for many of the same reasons. I originally was looking around at 3rd party libraries, but one of my goals with the site was to use as few external libraries as possible. I made an attempt at dynamically generating the svg points myself, and found it incredibly easy.

For context, here's the 22 lines of code it took to create a simple svg graph: https://github.com/jjcm/soci-frontend/blob/master/components...

And here's the final output: https://non.io/Animation-example


I thought it'd be fun to play with sparklines on Val Town, which is a server-side platform for running JavaScript.

First I did the opposite of this post and used all the libraries (react, htm, and react-sparkline): https://www.val.town/v/stevekrouse.sparklineEx

Then I found a comment below that does it in vanilla js, so I ported that over to Val Town as well: https://www.val.town/v/stevekrouse.sparklineEx2


SVG feels like the neglected stepchild of the web universe for reasons that are not entirely clear.

While its obviously not the solution to any and all visualizations in the browser its a remarkable addition to html and the dom. It should not really be considered a foreign format but the natural native one.

SVG can go a long way on its own or together with server side templates (as this post nicely demonstrates) but imho its pairing with js libraries such as d3 or vega is (still) out of this world in terms of the user experience they create.


Javascript version here: https://jsfiddle.net/buk3dsqv/


It would be nice if you consider to put it in a jsfiddle, or something.


Good point, updated that to a jsfiddle.


I ported your jsfiddle to run on val town!

Main function: https://www.val.town/v/stevekrouse.sparklineSVG

Rendered example: https://www.val.town/v/stevekrouse.sparklineEx2


Thank you ♥


Cool explanation! I use quite a lot of SVG visualisations generated server-side. Some basic charts (including sparkline) are bundled up into ContEx (an elixir library) - see https://contex-charts.org/ (disclosure - author).


Thanks! And thanks also for your work on ContEx, its Sparkline module [1] was a big inspiration for what I ended up implementing.

[1] https://github.com/mindok/contex/blob/master/lib/chart/spark...


I've used a similar technique to implement one of the graphs for small "header dashboard" for a trading tool at my previous job. It was replacing an old decrepit tool and I wanted to add some pizzaz to the tool, and had an obsession with SVG and micro interactions at the time, so I've basically implemented most of the little graphics using hand-emitted SVG that was manipulatied through React. The updates happened in one batch too, so the performance was always great


this looks like a subject I like to read on a blog


This is fantastic. I'm already using string interpolation & builders for my HTML/JS/CSS source, so why not the same for SVG?

I didn't realize the syntax was so straightforward. It looks like you could even build the final SVG with some clever SQL queries if all you need to do is produce a time series visual (i.e. string aggregation over Path).


I'm going to be the Smug Lisp Weenie here (I wonder who gets the reference), and comment on this:

> One of my favourite things about creating sparklines like this is that I can create the SVGs entirely on the backend. I don’t need to worry about using a JavaScript charting library, or sending the “points” data to the frontend. The browser requests an SVG. The server returns it. Simple!

Me, I don't care where I create the SVGs. Most of my Clojure code is shared between backend and frontend (compiled and running on the JVM in the backend and compiled to JavaScript in the frontend). So I can generate SVGs wherever, it doesn't matter, the code is only written once. Or rather, it might matter, because my website uses server-side rendering, so the same thing must be generated on both sides.

I'll see myself out now.


I mean... isomorphic code isn't unique to Clojure & is how many JS/NodeJS apps work...


Of course. But I'm a Smug Clojure Weenie, so. :-)


Fair


> I wonder who gets the reference

https://news.ycombinator.com/user?id=kennytilton sure does. :D


What's Clojure's WASM story like? That would be much more exciting than having to transpile to JS.


Wasm is not an good compile target for it. It's made for C/C++/Rust type static, native-code languages. You would basically have to build the same kind of runtime environment implementation as you'd need for a native-code managed VM.

(That's why for example Python works well on WebAssembly - it comes with its own interpreter written in portable C that compiles nicely to WebAssembly, and uses reference counting for GC instead of a fancy GC).

Since Clojure is mainly a hosted language, a more likely avenue would be one of its current platforms gaining WebAssembly support. A Wasm JVM, or Wasm GraalVM target, for example.


Why would it be more exciting?

I've been running a massive SaaS app for the last several years. JavaScript is not a performance problem, especially after compiling using the Google Closure compiler "advanced" mode. Rendering is a performance problem: think large tables. WASM would bring me no significant advantages.


I've always thought it would be neat to use sparklines to display trends in comment scores. You could see if a comment was monotonically being voted up or down or if it's considered controversial, and if so, to what extent. It would help distinguish between bandwagon/brigading activity and genuine organic rejection or appeal.

And it's often interesting to see trends or cycles emerge as people in different geographical regions wake up and log on. Some comments play much better in the US than in Asia or the EU and vice versa, and sparklines would be a good way to observe that.


Cool idea! ChatGPT-4 was able to generate something for me pretty quick https://chat.openai.com/share/d0e8102c-9ea5-4709-8898-b278ca... that looks pretty similar https://jsfiddle.net/13awmx8y/


love it and tried it on node-red to add a simmple sparkline to my dashboard


Ah! So all that complicated looking syntax is just a turtle with a pen.


This references Logo, a programming language where you control a turtle to draw shapes.

https://en.wikipedia.org/wiki/Logo_%28programming_language%2...


this works well when you have a few sparklines with a dozen datapoints each, but less well when you have many sparklines with hundreds of datapoints.


I'd bet an SVG <path> can easily handle hundreds, if not thousands, of datapoints.


Then you have to deal with scaling, responsiveness, data decimation, etc. It's not really a good idea to just have a multi thousand point line chat rendered real tiny


If you know what size you're dealing with it's not a huge deal. You can quantize the data and for (small) sparklines that's probably fine because most people view them as miniature glimpses at data rather than granular and accurate.


+1, the whole point of a sparkline is to compactly show a trend. Packing a gazillion data points is not the right use


Totally. I just mean that logic has to live somewhere, whether in client-side JS or a server. It's not a great practice to just blindly render SVGs out of raw data.


Aha, I misunderstood — I'd thought you were thinking of rendering multiple small SVGs.

Good point, although decimation for sparklines shouldn't be that difficult: I've done completely stupid decimation (recursively split stopping whenever linear fit is close enough) for live GPS traces and (because people are only using them qualitatively) no one ever complained.


it can for sure "handle" it. the question is "how well"?

a sparkline with 100 datapoints is very realistic. and having a couple columns in a table with 100 rows filled with these svg sparklines will have ui lag that you'll definitely feel.

sadly, svg is not a great performer in these 1k+ datapoints cases and you gotta switch to canvas.


You're populating a tiny template with a tiny list of numbers to produce a tiny plaintext document that will cache like Scrooge McDuck.

Why do you think this is going to be a problem?


pretty good. can user interactive charts be prepared in this way?


Yes, but depending on the interactions it can get complex quickly. D3.js handles all the interactions in JS on the client. ContEx (elixir server-side charting) handles certain events (e.g. data point click) server side (see https://contex-charts.org/barcharts - turn on “show clicked bar” option). Showing data point detail, e.g. “On hover” would require client side code.

(Disc: ContEx author)


Sure, but you'll need JS for anything complex. You could use SVG+CSS to show/hide specific things like data values on mouseover but it would be a global on/off for the whole chart.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: