s.times(25, () => {
  s.strokedPath((attr) =>
    attr.fill(
      s.sample([0, 20, 40]), 90, 50, 0.2)
  )
    .moveTo(s.randomPoint())
    .arcTo(s.randomPoint())
})

solandra-svg

A little library for drawing in SVG, but with a nicer API.

Basically I made this to generate stuff to plot. My first generated drawings for a 2D plotter. And my Second collection. And a Third collection. And a Fourth collection.

Try out

A ready to play with CodeSandbox.

GitHub

Full soure code for the library and this site

Install

npm install solandra-svg
yarn add solandra-svg

Tiling

Quickly create graphics that tile the canvas without having to worry about all the low lever details.

s.forTiling(
  { n: 5, type: "square", margin: 0.1 },
  ([x, y], [dX], c, i) => {
    const path = s
      .strokedPath((attr) =>
        attr.stroke(355, 10, 10, 0.9).fill(340, 90, 70, 0.2)
      )
      .moveTo([0.5, 0.5])

    s.times(10, () => {
      const pt = s.randomPoint()
      path.lineTo([x + pt[0] * dX, y + pt[1] * dX])
    })
  }
)

Hello Curves

Curves are easy and fun to draw with an API from Solandra that actually makes sense.

s.times(15, () => {
  let start = [s.random(), bottom] as Point2D
  let end = [s.random(), bottom] as Point2D
  s.strokedPath((attr) => attr.stroke(20, 90, 60, 0.5))
    .moveTo(start)
    .curveTo(end, { curveSize: 3 })

  start = [s.random(), 0] as Point2D
  end = [s.random(), 0] as Point2D
  s.strokedPath((attr) => attr.stroke(0, 90, 60, 0.5))
    .moveTo(start)
    .curveTo(end, { polarlity: -1, curveSize: 3 })
})

Hello Rectangles

Rectangles are easy to draw and the framework takes care of alignment.

s.times(25, () => {
  s.strokedPath((attr) => attr.fill(220, 90, 50, 0.2)).rect(
    s.randomPoint(),
    s.gaussian({ sd: 0.05, mean: 0.2 }),
    s.gaussian({ sd: 0.1, mean: 0.3 })
  )
})

Hello Ellipses

Ellipses are easy to draw and the framework takes care of alignment.

s.times(35, () => {
  const size = s.gaussian({ sd: 0.2, mean: 0.25 })
  s.strokedPath((attr) =>
    attr.fill(s.sample([130, 200, 210]), 90, 40, 0.2)
  ).ellipse(s.randomPoint(), size, size / 1.25)
})

Hello Chaiken

An elegant algorithm for smooth a path of lines. Repeatedly cut the corners. In solandra-svg this is only applied to lines (as it doesn't make sense for other path segments).

const { bottom } = s.meta
s.times(4, (n) => {
  const path = s
    .strokedPath((attr) =>
      attr.strokeOpacity(0.2 + n * 0.1).stroke(15, 90, 60)
    )
    .moveTo([0.1, bottom * 0.4])
  for (let i = 0.1; i <= 0.9; i += 0.2) {
    path.lineTo([i, bottom * 0.4 + 0.3 * Math.cos(i * 10)])
  }

  path
    .map((el) => {
      if (el.kind === "line" || el.kind === "move") {
        return { ...el, to: v.add(el.to, [0, 0.1 * n]) }
      } else {
        return el
      }
    })
    .chaiken(n + 1)
})

Hello Transforms

s.strokedPath((attr) =>
  attr
    .fill(210, 90, 20, 0.5)
    .transform(new Transform().rotate(Math.PI / 8))
).rect([0.3, 0.3], 0.2, 0.3)

Hello Clone

const path = s.strokedPath().ellipse([0, 0], 0.3, 0.4)

s.times(20, (n) => {
  s.clonePath(path).configureAttributes((attr) =>
    attr
      .transform(new Transform().scale(n / 2, n / 2))
      .stroke(n * 5, 90, 40)
  )
})

Hello Groups

solandra-svg offers a closure based API for building svg groups. You use the same fluent Attributes api to set up their attributes.

const { center } = s.meta
s.times(8, (n) => {
  s.group(
    Attributes.stroked.transform(
      Transform.of({ translate: center, scale: (4 + n) / 14 })
    ),
    () => {
      s.path(
        Attributes.of({
          opacity: (8 - n) / 10,
        })
      ).rect([0, 0], 1, 1)

      s.path(
        Attributes.of({
          stroke: { h: n * 4, s: 90, l: 50 },
          transform: Transform.of({ rotate: n }),
        })
      ).ellipse([0, 0], 1, 0.8)
    }
  )
})

The full code for all these examples is available on GitHub.