[HN Gopher] Easy SVG sparklines
       ___________________________________________________________________
        
       Easy SVG sparklines
        
       Author : alexpls
       Score  : 296 points
       Date   : 2023-07-10 10:59 UTC (12 hours ago)
        
 (HTM) web link (alexplescan.com)
 (TXT) w3m dump (alexplescan.com)
        
       | Lockal wrote:
       | 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
        
         | dspillett wrote:
         | 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.
        
         | lazyjeff wrote:
         | 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
        
         | xyzzy_plugh wrote:
         | 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.
        
           | XCSme wrote:
           | > You know what's even faster than Canvas? JPEG
           | 
           | Is that true? Isn't canvas a bitmap whereas JPEG requires
           | decoding?
        
             | jancsika wrote:
             | What's the fastest way to deliver the bitmap to the canvas?
        
               | XCSme wrote:
               | Hardcode the RGB array?
        
         | thehappypm wrote:
         | Hard to imagine that pure-HTML SVG is really slower than Canvas
         | which relies on JS..
        
           | TeMPOraL wrote:
           | Especially if we're talking using "many hundreds or even
           | thousands of them" on a page.
        
           | klysm wrote:
           | Canvas is faster _because_ it relies on JS. It's a much
           | dumber and stateful API - the browser has to do much less
           | work.
        
           | fuzzy2 wrote:
           | 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).
        
             | thehappypm wrote:
             | How do you draw an image on a canvas without JS?
        
               | fuzzy2 wrote:
               | 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.
        
           | Lockal wrote:
           | 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.
        
             | thehappypm wrote:
             | Guess it depends on your definition of performance.
        
         | zagrebian wrote:
         | In that case, browsers should optimize SVG more.
        
           | klysm wrote:
           | SVG could never be as fast as canvas.
        
             | llm_nerd wrote:
             | 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.
        
               | klysm wrote:
               | Canvas is just a pixel buffer. Even with sufficient
               | caching SVG would be slower because there's more work to
               | do with parsing, etc.
        
               | llm_nerd wrote:
               | 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.
        
               | postalrat wrote:
               | SVG is a pixel buffer the same way html is a pixel
               | buffer. It can have multiple types of animations, hover
               | states, etc.
        
               | klysm wrote:
               | 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
        
         | drewcoo wrote:
         | > The main use case for sparklines is embedding them into cells
         | 
         | Says who? Citation?
        
         | tripflag wrote:
         | 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.
        
           | dylan604 wrote:
           | 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?
        
             | tripflag wrote:
             | 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 :-)
        
             | josephcsible wrote:
             | This kind of thinking is how we ended up with the IE6
             | disaster.
        
               | dylan604 wrote:
               | That's my point of having the logic to do multiple
               | workflows based on browser type.
        
         | wryanzimmerman wrote:
         | 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.
        
           | esafak wrote:
           | A spreadsheet with numerous sparklines per row would get you
           | there.
        
       | umtksa wrote:
       | love it and tried it on node-red to add a simmple sparkline to my
       | dashboard
        
       | CamperBob2 wrote:
       | 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.
        
       | bencevans wrote:
       | 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
        
       | leeoniya wrote:
       | 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.
        
         | Gordonjcp wrote:
         | 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?
        
         | 082349872349872 wrote:
         | I'd bet an SVG <path> can easily handle hundreds, if not
         | thousands, of datapoints.
        
           | solardev wrote:
           | 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
        
             | nkozyra wrote:
             | 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.
        
               | thehappypm wrote:
               | +1, the whole point of a sparkline is to compactly show a
               | trend. Packing a gazillion data points is not the right
               | use
        
               | solardev wrote:
               | 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.
        
               | 082349872349872 wrote:
               | 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.
        
           | leeoniya wrote:
           | 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.
        
       | yawnxyz wrote:
       | 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
        
         | watersb wrote:
         | 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
        
         | pmarreck wrote:
         | 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
        
           | watersb wrote:
           | 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/
        
       | dreadlordbone wrote:
       | Javascript version here: https://jsfiddle.net/buk3dsqv/
        
         | entropie wrote:
         | It would be nice if you consider to put it in a jsfiddle, or
         | something.
        
           | dreadlordbone wrote:
           | Good point, updated that to a jsfiddle.
        
             | entropie wrote:
             | Thank you
        
             | stevekrouse wrote:
             | 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
        
               | [deleted]
        
       | jwr wrote:
       | 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.
        
         | mighmi wrote:
         | What's Clojure's WASM story like? That would be much more
         | exciting than having to transpile to JS.
        
         | lucideer wrote:
         | I mean... isomorphic code isn't unique to Clojure & is how many
         | JS/NodeJS apps work...
        
       | javajosh wrote:
       | 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.
        
         | bla3 wrote:
         | Doesn't this flip all text in the SVG upside down?
        
           | javajosh wrote:
           | Alas, yes. And you have to reflip it with another g/scale.
           | But such is life!
        
             | ComputerGuru wrote:
             | 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?)
        
       | chefandy wrote:
       | 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.
        
         | wbobeirne wrote:
         | 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.
        
           | watersb wrote:
           | 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
        
         | psygn89 wrote:
         | 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/
        
           | chefandy wrote:
           | 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.
        
         | javajosh wrote:
         | _> 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
        
           | chefandy wrote:
           | 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.
        
           | gabereiser wrote:
           | +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
        
       | jjcm wrote:
       | 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
        
       | nnf wrote:
       | 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.
        
         | kongprofit wrote:
         | do you have a blog
        
         | unmole wrote:
         | SVGs are amazing for interactive visualisation too. Like
         | Flamegraphs: https://www.brendangregg.com/flamegraphs.html
        
         | antonhag wrote:
         | 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.
        
       | drewcoo wrote:
       | 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.
        
       | pictur wrote:
       | pretty good. can user interactive charts be prepared in this way?
        
         | mindok wrote:
         | 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)
        
         | nkozyra wrote:
         | 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.
        
       | renewiltord wrote:
       | 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/
        
       | spoiler wrote:
       | 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
        
         | umtksa wrote:
         | this looks like a subject I like to read on a blog
        
       | stevekrouse wrote:
       | 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
        
       | [deleted]
        
       | nologic01 wrote:
       | 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.
        
       | bob1029 wrote:
       | 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).
        
       | mindok wrote:
       | 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).
        
         | alexpls wrote:
         | 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...
        
       ___________________________________________________________________
       (page generated 2023-07-10 23:00 UTC)