Apple Style Wallpaper with Solandra

Let's learn Solandra by making an image somewhat like those in Apple's iOS 13 update. Like in iOS you can toggle between dark and light modes.

To start with let's see how to draw a simple regular polygon. Solandra has a library of paths, each which can then be drawn (outline) or filled with draw or fill.

(s: SCanvas) => {
  s.background(0, 0, dark ? 10 : 95)
  s.setFillColor(0, 70, 40)
  s.fill(new RegularPolygon({ at: s.meta.center, n: 6, r: 0.3 }))
}

If we want more control we can create a path manually. Here we create a SimplePath (a series of points, joined by straight lines). We use Solandra's range function to iterate from 0 to N (inclusive), adding a point around a circle. This is basically what the regular polygon does, but not we have a fine level of control.

(s: SCanvas) => {
  s.background(0, 0, dark ? 10 : 95)
  s.setFillColor(0, 70, 40)

  const N = 14
  const r = 0.25
  const p = SimplePath.withPoints([])
  s.withTranslation(s.meta.center, () => {
    s.range({ from: 0, to: N, n: N }, n => {
      const a = (n * Math.PI * 2) / N
      p.addPoint([r * Math.cos(a), r * Math.sin(a)])
    })
    s.fill(p)
  })
}

We use that control to alter the radius of each point, giving us a irregular polygon. And then we smooth out this polygon by applying the Chaiken algorithm 3 times. This gives us a random blob. Because this process is random we can generate an indefinite number of variations by changing the random seed (click on the button below).

(s: SCanvas) => {
  s.background(0, 0, dark ? 10 : 95)
  s.setFillColor(0, 70, 40)

  const N = 24
  const r = 0.4
  const p = SimplePath.withPoints([])
  s.withTranslation(s.meta.center, () => {
    let rr = r
    s.range({ from: 0, to: N, n: N }, n => {
      const a = (n * Math.PI * 2) / N
      s.proportionately([
        [2, () => (rr += 0.02)],
        [2, () => (rr -= 0.02)],
        [1, () => (rr -= 0.04)],
        [1, () => (rr += 0.04)],
      ])
      p.addPoint([rr * Math.cos(a), rr * Math.sin(a)])
    })
    s.fill(p.chaiken({ n: 3, looped: true }))
  })
}

Finally, we assemble several blobs, placed randomly in our canvas to produce our final wallpaper. Change the seed. If you find one you like the button below gives you a high resolution version that you can save in Chrome (by right clicking).

(s: SCanvas) => {
  s.background(0, 0, dark ? 10 : 95)
  const N = 24
  s.times(10, n => {
    const c = s.randomPoint()
    const rotation = s.random() * Math.PI * 2

    const p = SimplePath.withPoints([])
    s.withTranslation(c, () => {
      s.withRotation(rotation, () => {
        let rr = s.sample([0.45, 0.35, 0.3])
        s.range({ from: 0, to: N, n: N }, n => {
          const a = (n * Math.PI * 2) / N
          s.proportionately([
            [2, () => (rr += 0.02)],
            [2, () => (rr -= 0.02)],
            [1, () => (rr -= 0.04)],
            [1, () => (rr += 0.04)],
          ])
          p.addPoint([rr * Math.cos(a), rr * Math.sin(a)])
        })

        if (n < 5) {
          s.setFillColor(
            0,
            10,
            dark ? s.sample([15, 20]) : s.sample([80, 85]),
            0.65
          )
        } else {
          const h = s.sample([0, 5, 10, 15, 20, 25])
          s.setFillGradient(
            new RadialGradient({
              start: c,
              end: c,
              rStart: 0,
              rEnd: 0.6,
              colors: [
                [0, { h, s: 70, l: 60, a: 0.98 }],
                [1, { h, s: 60, l: 50, a: 0.85 }],
              ],
            })
          )
        }

        s.fill(p.chaiken({ n: 3, looped: true }))
      })
    })
  })
}

(You can save this in Chrome. It will be a nice high resolution size for wallpaper use.)