any tips to speed up sketch with 'high' number of particles?

edited July 2016 in p5.js

I've got around 2800 particles (just calculating a couple of simple values and drawing them) but the sketch becomes quite slow (in Chrome and Firefox) ... I supposed that's to be expected? Or are there general tricks to speed something like this up?

Comments

  • Try using PShape for the particle. See this tutorial.

  • why would a PShape be faster than an ellipse() ?

  • If you read the tutorial I linked to it explains it. I am no expert but apparently with PShape the geometry is pre-calculated and its then used for each particle. With ellipse() the shape has to be calculated for each particle.

  • @quark ...but PShape doesn't appear to be available in p5.js yet :(|) :P

    @Xeronimo74 - you could instead try using an image for your particle for the exact same reason: you remove any processing overhead in terms of calculating each individual ellipse; but I've no idea how well optimised p5's image object is.

    Note that performance in p5 is a lot more limited than in pure Processing; largely because it's running in a browser. You will however see a significant performance boost if you use the webgl renderer - see createCanvas() - though note that this is browser/hardware dependent; so won't be available to everyone.

    To get a sense of the performance gain (assuming your browser supports webgl) see these two examples:

    1. p5 + canvas renderer: 2240 particles. Runs like treacle
    2. pixiJS + webgl: 9072 particles. Super smooth 60fps

    pixiJS claims to be the fastest 2D JS graphics engine out there; and certainly appears to deliver on the promise; but you do have to make compromises: e.g. it's optimised for use with image sprites and not primitives. Forcing the p5 sketch above to use webgl definitely improves performance significantly and might suit your needs. I'm mostly experimenting with pixiJS these days :ar!

  • edited July 2016

    I also did an attempt based on @blindfish's p5.js sketch source in this old forum below:
    https://forum.Processing.org/two/discussion/13845/continual-restful-request-to-json-on-server-parse-on-the-fly-render-data-inside-p5js-draw-loop

    Got an online version too: http://p5js.SketchPad.cc/sp/pad/view/ro.80gqevzuan$/latest

    Notice that WebGL mode isn't working for my sketch unless it's run under p5.js version 0.4.5:
    https://cdnjs.CloudFlare.com/ajax/libs/p5.js/0.4.5/p5.js

    Seems like it fails to image() p5.Image objects created outta its method get() under WebGL! ~X(

    @blindfish's p5.js orginal sketch is also relying on p5.js version 0.4.5! :(|)

  • Thanks, I'll have a look at all this! pixiJS is not really an option right now though since I'm drawing stuff to a canvas (that's not being refreshed), not using sprites.

  • Hm, when I add the 'webgl' argument to createCanvas() then I get the following error:

    30677: Uncaught TypeError: Cannot read property '_applyColorBlend' of undefined

  • it seems like it's related to the fill() command but I don't know why ...

  • but PShape doesn't appear to be available in p5.js yet

    oops ... my mistake should have looked at the category :\">

  • Thanks, I'll have a look at all this! pixiJS is not really an option right now though since I'm drawing stuff to a canvas (that's not being refreshed), not using sprites.

    @Xeronimo74: I'm not sure if you've fully understood so I'll labour the point. Whether you use pixiJS or p5 if you can remove any amount of rendering overhead up-front then you will see a performance benefit. Both p5 and pixi allow you to pre-render the results of a drawing operation to a graphics buffer (essentially a sprite). Whether the canvas is being refreshed is irrelevant: if your particle repeats the same graphic 1000s of times and this graphic isn't itself subject to change then pre-rendering in setup will give you a performance boost. You could even pre-render a set of frames in the case that the graphic needs to be animated.

  • blindfish: I think I got that.

    What I'm doing here is that a lot of agents (2000+) are drawing tiny semi-transparent circles to the screen that doesn't get refreshed, thus leaving trails. see the result here: https://ello.co/p5art/post/ah9bbzseicqqwetmnfm7nq

  • In that case - assuming the transparency is constant on all particles - a semi-transparent circle can be pre-rendered as an image or even set up in advance as a PNG file you load in...

    It looks like you might have been right about pixiJS in terms of preserving drawing operations on the canvas: it looks like it assumes you clear the stage after each frame (as it's predominantly used for animating sprites); but I imagine it would be possible to achieve the same result by drawing to a graphics buffer. When I get time I'll look into that ;)

  • it looks beautiful!

  • @Chrisir: thank you!

    @blindfish: pre-rendered using createGraphics()? I've tried that. Created one such image in setup() (and at certain intervals later on) that I'm referring to in the paint() function of my particles. No big change in speed though it seems ... am I not doing this correctly?

  • @Xeronimo74 - I found a simple way to implement a graphics buffer in pixiJS and I've just thrown together a quick and dirty proof of concept that draws the traces of 10000 particles to an image layer. On a high-end machine with WebGL support it runs at 60fps :)

    It's likely that your particle movement code is somewhat more complex than mine and that will of course slow things down significantly; but I'd expect you could get equivalent performance with the number of particles you're aiming for.

  • You can just record and play back as a movie

  • @blindfish: cool! would you mind sharing the code?

    @chrisir: yes but not in this case (it's a generative painting and I want it to be generated quicker ;))

  • @Xeronimo74 I didn't minify the source code so you can take a peak. I'm using Browserify to bundle my code together so the raw source is only useful if you are comfortable using that... In theory you could work directly with this bundled code; but I'd highly recommend Browserify or similar.

    Note that I re-used some code from another sketch; so could do with removing some extraneous stuff (might do that now in fact).

    As you'll see it doesn't take a lot of code and it would be trivial to replace my particle code...

  • @blindfish: ok, thanks a lot! I had a look at your code and it makes quite a lot of sense. I really have to look into PIXI.

    I'm not sure though I've understood what Browserify is for ...

  • @Xeronimo74 - I've just added some (optional) additional complexity to the particle behaviour of my proof of concept to see if this affects performance. No noticeable impact at my end :D

    Browserify is a tool that makes it easy to modularise your JS code using CommonJS modules (as is standard for NodeJS). All the boilerplate required to manage dependencies and protect scope is handled by Browserify: that means you write less code and it's easier to maintain.

    It makes it easy to break code down into re-usable snippets and to manage your code-base in separate files: that makes it easier to manage and re-use dependencies. So you can easily set up your own library of utility functions and pull them into projects as required.

    Most importantly it allows you to build modular/functional code thereby avoiding the pitfalls of class-based languages <(warning: highly opinionated article))

    It's quite frankly awesome and now I've started using it I don't ever want to go back; though I may go sideways (note that there are of course alternatives to Browserify: see Webpack etc..

  • edited July 2016

    I've also found the code a little confusing for study purposes.
    Though more powerful, Pixi.js is by itself harder than p5.js.
    However, by throwing Browserify (or any other packager) into the mix blurs the code further. :-&
    IMO, for such small sketches, there's no real need to apply advanced modularization.
    The examples I post in the forum tries to stay as bare-metal as possible, so folks can run'em as direct as possible on their browsers or IDEs. O:-)

  • @GoToLoop: ROFL. Your code examples - often for newbies - frequently include obscure optimizations and bleeding edge constructs (sometimes with limited browser support); often with little or no explanation... Talk about the pot calling the kettle black :P

    I deliberately chose not to post my source code on the forum: partly because it uses Browserify (the code I do post on the forum is always free of build tool dependencies); but mainly because this is the p5.js forum and my code is written for pixi.js ;)

    It's also a proof-of-concept that I threw together rather quickly and may need to tidy further. As such it was not directly intended as reference material.

    Still, if it helps, I've added comments to mark where my module code begins and ends; anything else is added by Browserify during the build process. main_sketch is where you'll find the setup code and animation loop - essentially equivalent to p5's setup() and draw(). Everything else is module code.

    IMO, for such small sketches, there's no real need to apply advanced modularization.

    Why not? If you apply functional programming practices to your modules (I'm still working on that!) you can build a set of utility modules that can be pulled into any sketch (whether they use p5.js or pixi.js or any other library) with a simple require(). This saves you repeating the same old code; or otherwise having to rely on complex, inflexible class hierarchies to achieve the same goal.

    Also once you figure out Browserify you'll realise there's nothing 'advanced' about applying it - on the contrary it makes like much easier :)

  • edited July 2016

    Talk about the pot calling the kettle black.

    LoL! >:) But I'd say that any functional style is obscure for most programmers! :P

    Why not? If you apply functional programming practices to your modules...

    Only your Pixi.js example isn't a module in which we can import to use in another code; but a whole program! 8-|

  • It's a quaint expression isn't it :)

    Any file that ends with module.exports = someFunctionOrObjectName; is actually a module file which can be required with Browserify. I can't guarantee that all the modules will work independently at this point though - and there are cross-dependencies...

Sign In or Register to comment.