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

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:

1 Like

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?

1 Like

Following up on this, I actually went ahead and created a small, header-only library to render thick 2D paths using OpenGL!

Here’s my forum thread: Drawing paths using OpenGL: Polyline2D

Here’s what it looks like:

Github: Polyline2D
Example JUCE App: Polyline2DExample


I’m running a spectrum analyser using Path.lineTo without any issues here. I use it for development/debugging purposes so it’s not about performance. However it runs smoothly and I’m using FFTSizes up to 8192. I think there is something else taking cpu - did you run some profiling to check for the bottleneck?

i’m running into a similar problem to the author. How did you manage to have 8192 and running smoothly?

I’m running a simplest plugin with the below inside Paint method and my CPU spikes up 70-80%.

 path.startNewSubPath ( 0, 0);
 for (auto i = 0; i < 2048; ++i)
     auto randY = random.nextInt (juce::Range<int> (0, getLocalBounds().getHeight()));
     path.lineTo ( i, randY);
 g.strokePath(path, juce::PathStrokeType (.1));

What is your trick to make this run smoothly?

It’s just not gonna happen using juces paint method. You need to run your component as an OpenGLRenderer, then in the render() callback, do the whole vertex buffer thing to draw in opengl code. For a line you’d use draw line strip.

This has been a hot topic for a while now, I wonder if the JUCE team is any closer to making the default paint routines any more suitable for dynamic components?

OpenGL is massiely overkill for this IMO.

@MarcelK, do you really need 2048 points? Even if your window was 2048px wide that’d be 1 point per pixel which really isn’t necessary unless you need that level of precision. I usually plot a point around every 5px for a spectrum analyser so for a window size of say 500 px that’s only 100 points which JUCE’s software renderer can handle easily.

1 Like

I thought that this would be the case. But placing:

openGLContext.attachTo (*getTopLevelComponent());

is not making any difference.
Is somewhere out there any code example to illustrate how to use render() callback?

Yes, I kind of need even 8192. I’m building a multichannel frequency spectrum plugin and so that users can choose from different sizes of FFT.

You’re going to have multiple points per pixel though, that’s quite literally impossible to see… You should just find the min and max points within a given range and draw a line for each x-position which will bring your points count down a lot.

Right, so the idea is to still be able to choose whatever FFT size and then calculate the min and max?
What’s the unspoken rule to calculate (smooth) the frequencies…is it from 2kHz upwards?

But, wouldn’t then let’s say 2048 and 4096 look the same after smoothing?

The higher FFT sizes really only improve the low-end precision, the added precision in the high-end is really not needed - especially if you’re displaying the frequency content with a log scale.

With an FFT size of 8192, and a sample-rate of 48kHz, you’ll have a frequency bin every 48000/8192 = 5.86Hz (I think my math is right? please correct me if that’s wrong). In the high-end, 6Hz difference is completely undetectable by human hearing (10kHz - 10.006kHz for example) so you really don’t need that level of precision while at the low-end 6Hz is quite noticeable.

In GT Analyser I allow users to select of block size of anywhere from 1024 to 65536 but I have a fixed number of points of 256 (which is actually a max because repeated points are omitted) which still gives good precision across the spectrum.

1 Like

That makes more sense now. So in theory you really are plotting only as many points/pixel as the window width…(obviously in log scale)

1 Like

last question…do you use in the OpenGL context or just the pure JUCE rendering and Path drawing?

I experimented with simply attaching an OpenGLContext to the plugin’s UI but found the CPU usage actually increased.

1 Like

I think when I’ve done it in the past I determined what the minimum distance in the x-axis is between two points, then for every point in the spectrum between those two pixels I take the maximum value to determine the y-coordinate. This then works nicely regardless of zoom level etc.

As @ImJimmi says I’ve also seen an OpenGL context increase CPU, however only on Windows. On macOS, it generally seems to improve things. In the past we had an option in the plugin to enable the OpenGL context, we would default it the option on for macOS, and off for Windows.