# Opensimplex on Fire

One of the things you can use OpenSimplex2 noise for is some fun rendering effects. In this vignette you will see how you can create a fire effect with OpenSimplex2 noise. This means that we will going to render a sequence of images, that put together will form an animation resembling fire. I will try to show you each step along the way.

## Setting up OpenSimplex2 Space

We are going to use a matrix with values between 0 and 1 to represent an image of fire. Where cells with values of 1 are ‘hot’ and those with 0 are ‘cold’.

So before we can do anything meaningful, we start by defining some properties for the matrix. After loading the package library we define some properties for the animation and the matrix that will hold the image data.

We define `w` and `h` as the width and height of the matrix, respectively. The number of frames in the animations is set with `nframes`. We use `scale` to tweak the scale of the Simplex noise in the xy-plane (i.e., the plane of the image). We generate a `data.frame` of coordinates that we will use for sampling the OpenSimplex2 noise space and store in the image matrix. We assign it to the `coords` object.

We also define a nice fire palette for our image, and call it `fire_pal`. Finally, we setup the OpenSimplex2 space. We use the smooth (`"S"`) variant for our purpose. We define 3 dimensions: 2 dimensions for the xy-plane of the image, and 1 dimension to represent time. Note that we also use `set.seed(0)`. We do so to make this vignette reproducible.

    library(opensimplex2)
    w <- 100L
    h <- 100L
    nframes <- 100L
    scale <- .05
    coords <- expand.grid(x = scale*seq_len(w), y = scale*seq_len(h))

    fire_pal <- colorRampPalette(c("#000000", "#330000", "#FF0000", "#FFCC00", "#FFFFCC"))

    set.seed(0)
    space <- opensimplex_space(dimensions = 3)

## Hot Bubbles

First we start our animation by sampling the noise space along the time dimension. We store the sampled values in a matrix named `feed`. As the noise is scaled between -1 and 1, we rescale the values by adding 4 and dividing by 5. This way, each cell will have some base ‘heat’ (i.e., values >0). This already gives a nice animation, and feels a bit like the surface of the sun, but it doesn’t look like fire yet.

    speed1 <- .02
    for (i in 1:nframes) {
      time <- rep(i*speed1, nrow(coords))
      feed <- matrix(space$sample(coords$x, coords$y, time), w, h)
      feed <- (4 + feed)/5
      image(feed,
            axes = FALSE, ann = FALSE, xaxs = "i", yaxs = "i",
            zlim = c(0, 1), col = fire_pal(100))
    }

Hot bubbles

## Heat Gradient

In order to make it look more like fire, we introduce a `cooling` matrix. It has values scaled between 0 at the top, up to 1 at the bottom. By multiplying the ‘hot bubbles’ from the previous step with this cooling gradient, it will cool down pixels at the top of the plot.

    cooling <- matrix(1 - seq_len(h)/h, w, h, byrow = TRUE)
    for (i in 1:nframes) {
      time <- rep(i*speed1, nrow(coords))
      feed <- matrix(space$sample(coords$x, coords$y, time), w, h)
      feed <- (4 + feed)/5
      image(feed*cooling,
            axes = FALSE, ann = FALSE, xaxs = "i", yaxs = "i",
            zlim = c(0, 1), col = fire_pal(100))
    }

Heat gradient

## Adding Movement

It looks more like fire already. But we are not there yet. It feels a bit static, whereas real flames would move upwards. To achieve this, we need to scroll across the xy-plane in the noise space. For this purpose we introduce `speed2`, and move the sampling position each frame with `-speed2*i`.

    speed2 <- .05
    for (i in 1:nframes) {
      time <- rep(i*speed1, nrow(coords))
      feed <- matrix(space$sample(coords$x, coords$y - speed2*i, time), w, h)
      feed <- (4 + feed)/5
      image(feed*cooling,
            axes = FALSE, ann = FALSE, xaxs = "i", yaxs = "i",
            zlim = c(0, 1), col = fire_pal(100))
    }

Flames

There you have it. This looks like a nice fire. Of course there are many improvements possible. You could combine different scales in different noise spaces to give it a more dynamic feel. You could also make the path along which the noise is sampled more fickly. But I’ll leave that up to you, to experiment with that.

## Seamless Animation

The animation shown above has 100 frames as specified at the start. Once the last frame is reached it will jump back to the first frame. As the first and the last frame have a completely different state, you will see that the transition is not so smooth.

You can use OpenSimplex2 noise to create seamless animations. The trick is that you have to sample your noise space at coordinates that are the same at the end as at the start. So if you sample the space along a path, you could use a circular path to make sure you end up at the same position as where you started.

This trick was also applied when rendering the cow logo for this package (see source code.

In the example above we scroll along the y-axis in the xy-plane. But what if we have a cylinder and roll along its surface? That way, if we rotate 360 degrees, we end up with the same noise. To achieve this we need an extra dimension, as the cylinder is three dimensional, and we still need a time dimension. If we loop the time along a triangular function, we can also cycle these values indefinitely. All of this is demonstrated in the example below.

    space4d <- opensimplex_space(dimensions = 4)

    coords_cyl   <- expand.grid(x = scale*seq_len(w), i = seq_len(nframes))
    cyl_radius   <- 1
    time_scale   <- 2
    time_cycle   <- time_scale*2*abs(nframes/2 - 1:nframes)/nframes
    for (i in 1:nframes) {
      time <- rep(time_cycle[i], nrow(coords_cyl))
      feed <- matrix(space4d$sample(coords_cyl$x,
                                    cyl_radius*sin(2*pi*(i - coords_cyl$i)/nframes),
                                    cyl_radius*cos(2*pi*(i - coords_cyl$i)/nframes),
                                    time), w, h)
      feed <- (4 + feed)/5
      image(feed*cooling,
            axes = FALSE, ann = FALSE, xaxs = "i", yaxs = "i",
            zlim = c(0, 1), col = fire_pal(100))
    }

Seamless

## Sources of Inspiration

For more inspiration of what you can achieve with OpenSimplex2 noise, I’ve put together a list of resources.

- Wikipedia: Perlin noise: Although focused on Perlin noise, OpenSimplex2 can be used as an alternative for most applications listed there.
- State of the Art in Procedural Noise Functions: A nice research article describing applications of noise functions in general
- SimplexNoise Demystified: A more technical document describing how Simplex noise works.