Is Path slow? What is the best tool for spectrum analyser graph?


I’ve just made a spectrum analyser. And to draw the graph I used Juce Paint class, and Path.lineTo function. But for bigger resolution (more path points) it starts to be very slow and lag frienfly. Bigger resolution I mean something about for example 10000 points path.
Of course I also suppose maybe my code isn’t optimal. But I think it’s the Path and Paint class. Why I think that? I am not sure but for simply sine wave it works great even for 20000 points of path. But for more complex signal, like saw wave - which is still not very complex of course - but my analyser starts lagging. Why?


As juce uses a scanline renderer, rendering paths that intersects a lot with horizontal lines is slow(ish). Unfortunately a spectrum usually does that a lot while a sine wave doesn’t.

The usual thing to do with analyzers is to avoid creating a path with a lot more resolution than the screen. That means in the spectra case for high frequencies an average value of multiple fft bins is used and a path with maybe ~1000 points is used for rendering.

If you still want to render 20000 points you could try rendering into an image that is ninty degrees rotated and then rotate the image back. I never tried this (but maybe I should), but it should be a LOT faster for a spectrum which has precisely one y value per x value.

Personally I find it a bit annoying that juce uses horizontal scanline rendering as it’s mainly used for audio things and most audio graphs have a lot more horizontal line intersections than vertical ones. (spectrum/waveform/envelopes).


I use OpenGL for any substantial real-time visual feedback.
It’s not for the faint of heart, but the method I use is:

  1. Pass points locations to GPU
  2. Use geometry shader to generate lines with joints
  3. Use the fragment shader to anti-alias those lines base on line thickness and distance from edge

I have dozens of lines taking up a lot of space and are drawing at 60 fps. The CPU usage remains pretty low. That said I’d rather not know how much time development time I’ve spent just on rendering lines :sweat_smile:


Some SR products written directly with OpenGL. When it works, it’s much smoother.
However, in terms of compatability OpenGL especially with bad drivers under Windows could generate crashes the moment context being created.


Can you share some code? I’m facing a similar problem - juce::Path’s performance simply doesn’t cut it for me.
While I sorta know my way around OpenGL and shaders, I’m not really looking forward to implementing it all myself - any head-start would be appreciated :slight_smile:


There’s quite a lot. There are also changes you need to make to your overall graphics project for it to work (e.g. you can’t layer an OpenGL component on top of a component rendered with paint())

I’d take a look at how nanovg works as a starting point. I think some people on the forum here use it for line rendering:


Have you seen this?


That looks promising, given I can get it to work on my Macbook (I saw your issue on Github).
Perhaps I’ll create a simple, stripped-down open source library for OpenGL paths based on this!


Imho OpenGL is the way to go. I’d recommend using as low a feature set of OpenGL as possible, I’m usually going for 2.0 (even though that can be a pain, the really nice shader features arrived later). If you then test on AMD, nVidia and Intel, and it turns out to work fine on HD3000, you’re most likely safe. The only issue I had so far was with a customer who somehow managed to not install gpu drivers at all on his windows machine - and he didn’t know until our support figured it out :wink:


What platforms does nanoVG support? Will it also work on mobile/openGL ES? I guess Mac, Windows, Linux are no problem?


(e.g. you can’t layer an OpenGL component on top of a component rendered with paint())

How do you mean this? If I create a juce::Component, implement juce::OpenGLRenderer and attach it to an OpenGLContext and just render it, I seem to get some pretty good performance…

However, of course, my OpenGL component’s paint function is empty, which is fine, I can render everything with OpenGL.
Is there a different way I should go about it?