Expand description

Master branch online documentation is available at doc.icelk.dev.


First you need to get a Config. I suggest creating it like this:

let config = Config {
    iterations: 100_000_000,


Call render_parallel.

This benefits the most when the number of iterations is much higher than the dimensions of the image. The gap closes rapidly when the relation is < 25. This also consumes more memory, as we need a set of working images for each execution unit, usually the number of threads on your CPU. See Performance for more info.


Create a Runtime. Then render and finally colorize.


The thing slowing the algorithm down with larger image dimensions is the cache size - and memory access. We basically do random access reads and writes on a often > 2 megapixel image. If the system memory is slow, this brings performance to a halt.


When the iterations are executed, the magnitude of change is stored in a texture. When it’s time for colouring, this |Δp| is mapped to a palette. The brightness is determined by the number of visits to the pixel.



  • The included attractors.
  • Configuration for rendering - how to get the next iteration, colouring, camera position, brightness, etc.
  • Mathematical primitives


  • Handle to threads and channels to render a config on multiple threads.
  • Stores data used by the algorithm.



  • Render according to config, with angle rotation around the attractor.
  • I recommend 16 for jobs_per_thread. If you get uneven images with low iteration counts, try 8.

Type Definitions

  • Convenience alias for an 16-bit RGBA image.