npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lindsvg

v2.0.0

Published

Lindenmayer System [Scalable] Vector Graphics

Readme

lindsvg

lindsvg (pronounced /ˈlɪnds ˈviː ˈdʒiː/), Lindenmayer System [Scalable] Vector Graphics

Simple dependency-free module used to generate SVG images of deterministic L-systems.

Generated SVG tree

Installation

In Node.js environment

Installing the package:

npm install lindsvg

Once installed, it can be imported as an ES module:

import * as lindsvg from "lindsvg";

In a browser

You may get the module sources from such CDNs as unpkg or jsDelivr:

<script type="module">
  import * as lindsvg from "https://unpkg.com/lindsvg@2/";
  // ...
</script>

Supported commands

The following turtle commands are currently supported by lindsvg:

| Command | Description | | ------------------- | ----------------------------------------------------- | | F | Move forward one step with drawing a line | | B | Move forward one step without drawing a line | | + | Turn left by turning angle (theta) | | - | Turn right by turning angle (theta) | | \| | Reverse direction (turn by 180 degrees) | | ! | Reverse the meaning of + and - | | [ | Push current state of the turtle onto the stack | | ] | Pop a state from the stack and apply it to the turtle | | A,CE,GZ | Auxiliary user-defined rules |

API & examples

getSVGCode()

This method generates ready-to-render L-system’s SVG code as an HTML string.

Syntax

getSVGCode(lsParamsMap)
getSVGCode(lsParamsMap, svgParams)

Parameters

  • lsParamsMap

    Mapping of L-system key (user-defined name or identifier) to the L-system parameters. You can either specify one key-value pair if you’re creating an image of a single L-system, or provide multiple key-value pairs if you want to combine several independent L-systems in a single image. The values in this mapping are objects, each containing the parameters of one L-system:

    • axiom

      A string containing the initial codeword (axiom).

    • rules

      An object representing L-system production rules. The keys are the alphabet letters AZ, and corresponding values are the production successors (rewriting rules). Two symbols of the alphabet (F and B) have special meaning as explained in the “Supported commands” section.

    • x (optional)

      Turtle’s initial horizontal coordinate. Its default value is 0.

    • y (optional)

      Turtle’s initial vertical coordinate. Its default value is 0.

    • alpha (optional)

      Initial angle in radians. Its default value is 0.

    • theta

      Angle increment in radians.

    • step

      The length of the turtle’s step, a finite positive number.

    • iterations

      Total number of iterations used to generate the resulting L-system.

  • svgParams (optional)

    An object containing parameters that can be used to modify the appearance of the resulting SVG image. All of these parameters are optional.

    • width (optional)

      Desired width of the SVG image. If not specified, defaults to the image’s intrinsic width.

    • height (optional)

      Desired height of the SVG image. If not specified, defaults to the image’s intrinsic height.

    • padding (optional)

      Additional space to extend the viewBox. If the image content is drawn too close to the edges, you can add padding by setting the desired numeric value to this property. Its default value is 0.

    • pathAttributesMap (optional)

      Mapping of L-system key to the <path> element attributes. Keys in this mapping are the same as in the lsParamsMap argument. Values are objects mapping attribute names to attribute values. You can use this option to change such things as stroke color, line width, etc. The default attribute mapping is {fill: "none", stroke: "#000"}. For branched L-systems, attribute values can be specified as arrays, so that different values will be applied at different branching levels (see the dedicated section on advanced styling of branched L-systems).

    • templateFn (optional)

      Function producing the resulting SVG code from raw data. You can provide your own template with additional markup to customize the appearance of the final image. Using this option is explained in the dedicated section on SVG template customization.

Return value

A string containing the complete HTML code for the resulting SVG image.

Example: single L-system

The following example demonstrates the use of the method getSVGCode() to generate the L-system “Mango-tree foliage”.

import {getSVGCode} from "lindsvg";

// L-system parameters
let lsParamsMap = {
  "Mango-tree foliage": {
    axiom: "A---A",    // The initial codeword (axiom)
    rules: {           // L-system production rules
      F: "F",          // Move forward a step with drawing a line
      B: "B",          // Move forward a step without drawing a line
      A: "B-F+Z+F-BA", // Auxiliary rules...
      Z: "F-FF-F--[--Z]F-FF-F--F-FF-F--",
    },
    alpha: 0,           // Initial angle in radians
    theta: Math.PI / 3, // Angle increment in radians
    step: 15,           // The length of the turtle’s step
    iterations: 7,      // Total number of iterations
  },
};

// Output SVG parameters
let svgParams = {
  width: 600,          // Desired width of the SVG image
  height: 600,         // Desired height of the SVG image
  padding: 5,          // Additional space to extend the viewBox
  pathAttributesMap: { // Name to value maps for the <path> element attributes
    "Mango-tree foliage": {
      stroke: "green",
      "stroke-width": "2",
    },
  },
};

// Get L-system’s SVG code as a string and render the image
let svgCode = getSVGCode(lsParamsMap, svgParams);
document.body.insertAdjacentHTML("beforeend", svgCode);

Example: multiple L-systems

The following example demonstrates the use of the method getSVGCode() to generate the “Twindragon” curve containing two Heighway dragon curves placed back to back.

import {getSVGCode} from "lindsvg";

let lsParams = {
  axiom: "FX",
  rules: {
    F: "F",
    X: "X+YF+",
    Y: "-FX-Y",
  },
  theta: Math.PI / 2,
  step: 3.5,
  iterations: 14,
};

let lsParamsMap = {
  "Yellow-green Dragon": {
    ...lsParams,
  },
  "Forest-green Dragon": {
    ...lsParams,
    y: -448, // adjust vertical position of the 2nd dragon
    alpha: Math.PI,
  },
};

let svgParams = {
  padding: 15,
  pathAttributesMap: {
    "Yellow-green Dragon": {
      stroke: "YellowGreen",
    },
    "Forest-green Dragon": {
      stroke: "ForestGreen",
    },
  },
};

let svgCode = getSVGCode(lsParamsMap, svgParams);
document.body.insertAdjacentHTML("beforeend", svgCode);

getSVGData()

This method returns raw data that you can use to assemble the SVG code yourself. It may be handy in cases where full access to path data is required, or you need to fine-tune SVG parameters and content.

Syntax

getSVGData(lsParams)
getSVGData(lsParams, options)

Parameters

  • lsParams

    An object which contains L-system parameters. See getSVGCode() for details.

  • options (optional)

    An object for tuning the method’s behavior. Currently, one option is available:

    • isMultiPath (optional)

      This option is only effective for branched L-systems. Its default value is false which means that the method will always construct path data for a single combined <path> element representing the complete L-system. If the option is set to true, the method provides separate path data for multiple <path> elements, each representing a specific branching level.

Return value

An object which contains the following fields:

  • pathData

    An array of path data strings for each <path> element in the L-system SVG image. If the option isMultiPath is set to false or not specified, pathData contains only one element. L-systems without branching always produce pathData with a sole element.

  • minX

    A number defining the left drawing boundary. Essential for assembling the viewBox attribute.

  • minY

    A number defining the top drawing boundary. Essential for assembling the viewBox attribute.

  • width

    A number defining the intrinsic width of the SVG image. Essential for assembling the viewBox attribute.

  • height

    A number defining the intrinsic height of the SVG image. Essential for assembling the viewBox attribute.

Example

The method can be used to generate path data that is passed to the path() CSS function to achieve interesting visual effects. This example demonstrates the using getSVGData() for clipping an image to the Koch snowflake boundary.

import {getSVGData} from "lindsvg";

let lsParams = {
  axiom: "F++F++F",
  rules: {
    F: "F-F++F-F",
  },
  y: 117,
  theta: Math.PI / 3,
  step: 5,
  iterations: 4,
};
let {pathData, width, height} = getSVGData(lsParams);
let img = new Image(width, height);
img.src = "./winter-night.jpg";
img.style.objectFit = "cover";
img.style.clipPath = `path("${pathData[0]}")`;
document.body.appendChild(img);

Advanced styling

Branched L-systems

As mentioned earlier, the property pathAttributesMap of the svgParams option allows specifying attribute values in form of arrays. This allows you to provide different attribute values for each <path> element, which may make branched L-systems (like plants) look “more naturally”.

For example, to generate the tree demonstrated above (all but foliage) the following options were used:

import {getSVGCode} from "lindsvg";

let lsParamsMap = {
  "Demo tree": {
    axiom: "F-FFF-F+F+X",
    rules: {
      F: "F",
      X: "FFF-[-F+F[Y]-[X]]+[+F+F[X]-[X]]",
      Y: "FF-[-F+F]+[+F+FY]",
    },
    alpha: 90 * Math.PI / 180,
    theta: 14 * Math.PI / 180,
    step: 12,
    iterations: 6,
  },
};

let svgParams = {
  width: 565,
  height: 445,
  padding: 10,
  pathAttributesMap: {
    "Demo tree": {
      stroke: "#514d3a",
      "stroke-width": ["16", "11", "9", "7", "6", "5", "3", "2", "1"],
      "stroke-linecap": ["square", "round" /* all remaining items use the last specified value */],
    },
  },
};

let svgCode = getSVGCode(lsParamsMap, svgParams);
document.body.insertAdjacentHTML("beforeend", svgCode);

If an attribute array contains fewer elements than the maximum branching depth (see stroke-linecap in the example above), the missing items are implicitly made equal to the last specified one. So you don’t need to repeat the same value in the end of the list.

You may also use the special value "n/a" which prevents an attribute from being added on the corresponding <path> element (e.g. when you need to add an attribute only to one or to a few <path>s: {pathAttributes: {transform: ["skewY(-35)", "n/a"]}}).

SVG template customization

In normal cases, SVG image appearance is customized by providing presentation attributes via the pathAttributesMap option. However, there may be cases where attributes alone are not enough. For example, if you want to apply gradient stroke to your L-system, there will be need in extra-markup with the gradient definition. In such cases the special option templateFn can be handy. By redefining the SVG template, you may add whatever you need in the resulting code.

The example below demonstrates how gradient stroke style can be applied by providing a custom template function.

import {getSVGCode} from "lindsvg";

let lsParamsMap = {
  "L-system “Square”": {
    axiom: "F+F+F+F",
    rules: {
      F: "FF+F+F+F+FF",
    },
    theta: Math.PI / 2,
    step: 5,
    iterations: 4,
  },
};

let svgParams = {
  width: 407,
  height: 407,
  padding: 2,
  pathAttributesMap: {
    "L-system “Square”": {
      stroke: "url(#diagonal-gradient)",
    },
  },
  templateFn: ({viewBox, width, height, content}) => `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox.join(" ")}" height="${height}" width="${width}">
      <defs>
        <linearGradient id="diagonal-gradient" gradientTransform="rotate(45 0.5 0.5)">
          <stop offset="0%" stop-color="red" />
          <stop offset="50%" stop-color="gold" />
          <stop offset="100%" stop-color="red" />
        </linearGradient>
      </defs>
      ${content}
    </svg>`,
};

let svgCode = getSVGCode(lsParamsMap, svgParams);
document.body.insertAdjacentHTML("beforeend", svgCode);

Error handling

In case of invalid input L-system parameters, the methods throw a custom exception. You may use it to analyze which parameter(s) failed to pass validation, and format the error message as you wish.

import {getSVGCode} from "lindsvg";
import {dump} from "js-yaml";

try {
  console.log(getSVGCode(lsParamsMap, svgParams));
} catch (error) {
  // Log the original message
  console.error(error);
  if (error.name === "LSError") {
    // Get a JSON representation of the error list and format it as YAML
    let errorJSON = error.toJSON();
    console.log(dump(errorJSON, {indent: 4}));
  }
}

Demos

Please, visit the project’s demo web app (installable as a PWA and works offline too). You will find a few built-in L-system collections there, and will also be able to experiment with lindsvg while building your own L-systems.

Also, check out this collection on CodePen to get a few advanced examples of using lindsvg.