Hello, I have a question about efficiently using uniforms.

For a school project our assignment was to create fireworks using openGL 1.0 and freeglut. Basically, this involved an array of particles with a position and velocity etc. which would be updated every timer tick.
The particles are drawn by pushing and popping a matrix for each particle, since they each have a different position and size.

The project is over now but I had a blast making it and would like to update it to the modern shader approach. I ported the project using the glTools library from the book ‘OpenGL Superbible 5th edition’. This library loads shaders for you and you still have matrix push, pop, translate and scale methods like in 1.0, but behind the scenes it is using the shader approach with uniforms and attributes and the like.

However, now I am calling the shader for each particle, updating the mvp matrix for each particle and sending it through as a uniform. Doing this for each particle kind of defeats the point of uniforms as they are supposed to be values which are shared over a batch of objects (from what I understand of it).

What am I missing? How can I send through a batch of particles (which may be at very different positions) without calling the shader which multiplies the vertices with the mvp for each particle?
I apologize if this is a basic principle, I am very much a beginner at openGL 3.x and computer graphics in general.

they are supposed to be values which are shared over a batch of objects

Once you loaded a shader, a uniform will not change unless you explicitly change it. So you can use the shader program with multiple draw calls without reloading the uniforms.
In particular the projection matrix should not change. If you have multiple shaders and want to
share uniforms across them you need to use a shared uniform buffer (GL_UNIFORM_BUFFER).

Things that change with each vertex should be included in the vertex structure - in your case the location will need to be updated. It is possible to improve this performance by using transform feed back buffers.