Documentation

Get started

Mechanic* is a powerful open source framework that helps forward-looking designers move away from a manual design workflow by converting their design rules into tools.

To start working with Mechanic, you need to create a project. Run the following command in the terminal:

npm init mechanic@latest my-project

Make sure you have Node.js (version 12.20 or greater) and npm installed.

This will install the Mechanic command-line interface and prompt you with instructions on how to customize your new Mechanic project, including your very first design function. You can choose between creating a new design function based on a template or a more sophisticated example. As a result, you will end up with a folder with your chosen project name and the following files in it:
  • README.md contains some pointers on how to start using your Mechanic project
  • package.json contains all the dependencies needed to use Mechanic
  • mechanic.config.js contains the Mechanic configuration
  • functions/ is a folder that will contain your design functions

Design functions

Design functions are JavaScript functions that generate a design asset. They are the main building block of Mechanic, and a project can have one or more design function. Based on the content in the design functions, Mechanic creates a design tool you will be able to use in your web browser.

The generated asset can be either static or animated, simple or complex. A design function creates assets using a specific web technology and a corresponding Mechanic engine that enables it. Currently, the available options for engines are SVG, Canvas, React.js and p5.js.

Each design function definition should live in a separate folder in the functions/ folder. Each function folder will contain an index.js file with the following elements:

Main handler

This is where you write the code that produces your design, and this code will be specific to the chosen engine. A handler function always receives a single argument, and your code can produce variable designs based on the values in this object. Learn more about handler arguments.

export const handler = ({ inputs, mechanic }) => {

  //  Implementation of the design goes here

};

inputs

inputs is an object defining the variables that will be passed to your design function. Each input will create an element in the design tool UI, and users can change these inputs and run the function with updated values. Learn more about inputs.

// Specify as many inputs as you want
export const inputs = {
  input1: {
    type: "number",
    ...
  },
  input2: {
    type: "color",
    ...
  },
  ...
};

Presets

Presets give you the opportunity to set predefined sets of values for the inputs that users can select with a single click in the design tool UI. Learn more about presets.

// Specify as many presets as you want
export const presets = {
  preset {
    input1: 2,
    input2: "#454545",
    ...
  }
};

Settings

Settings allow you to set general configuration for your function. This is where you specify the engine that your design function will be using and whether the design function produces a static or animated asset. Learn more about settings.

// General configuration for your function
export const settings = {
  engine: require("@mechanic-design/engine_name"),
  animated: false,
  optimize: true,
};

Templates

Templates are simple design functions created to show you how to use Mechanic with specific web technologies for either animated or static assets. You can use one to get to know Mechanic, or use one as a base to start your design function.

Use the npm init mechanic@latest command to create a new Mechanic project with a single design function based on a template. Once you have created your Mechanic project, you can use the npm run new command to add more design functions.

The currently available templates you can find are:

  • Canvas image: Produce a static asset using the Canvas engine.
  • Canvas video: Produce an animated asset using the Canvas engine.
  • p5.js image: Produce a static asset using the p5.js engine.
  • p5.js video: Produce an animated asset using the p5.js engine.
  • React image: Produce a static asset using the React engine.
  • React video: Produce an animated asset using the React engine.
  • SVG image: Produce a static asset using the SVG engine.
  • SVG video: Produce an animated asset using the SVG engine.

Let's take a closer look at the p5.js video template, which produces an animated asset.

p5.js video

When using the p5.js engine, the handler function argument will have three properties: inputs are the values of the inputs defined below, mechanic holds API functions to record the frames of the animation, and sketch is the p5.js sketch that you can use to invoke p5.js functions. This last argument is specific to this engine.

export const handler = ({inputs, mechanic, sketch}) => {
  // Extract values of the inputs
  const { width, height, radiusPercentage } = inputs;
  const radius = ((height / 2) * radiusPercentage) / 100;
  let angle = 0;

  // The p5.js draw function is called 60 fps
  sketch.draw = () => {
    sketch.rotate(angle);
    sketch.arc(0, 0, 2 * radius, 2 * radius, -sketch.PI, 0);
    sketch.arc(0, 0, 2 * radius, 2 * radius, 0, sketch.PI);
    sketch.rotate(-angle);

The mechanic.frame() function is used to record each individual frame of the animation, while the mechanic.done() function tells Mechanic that the asset has finished rendering. You can read more about these API functions in handler arguments.

    if (angle < turns * 2 * Math.PI) {
      mechanic.frame();
      angle += (2 * Math.PI) / 100;
    } else {
      mechanic.done();
    }
  };
};

export const inputs = {
  width: {
    type: "number",
    default: 400,
  },
  height: {
    type: "number",
    default: 300,
  },
  radiusPercentage: {
    type: "number",
    default: 40,
    min: 0,
    max: 100,
    slider: true,
  },
  // ...
};

export const presets = {
  medium: {
    width: 800,
    height: 600,
  },
  // ...
};

The settings define that this design function uses the p5.js engine via the @mechanic-design/engine-p5 package. Also, since this asset is animated and uses the frame() method, this is specified in the settings too.

export const settings = {
  engine: require("@mechanic-design/engine-p5"),
  animated: true,
};

Examples

Examples are more complex design functions created to show you how to use Mechanic to tackle some more advanced use cases. The initial release of Mechanic has a single example, but more examples will be added along the way.

Use the npm init mechanic@latest command to create a new Mechanic project with a single design function based on an example. Once you have created your Mechanic project, you can use the npm run new command to add more design functions.

  • Poster generator

    This is an example of how Mechanic can be used to create a custom poster generator. Using the p5.js engine, this design function creates random event posters and enables the user to customize the text and image content. It also shows how to load custom webfonts to generate these posters. You will find this example embedded on the front page of this website.

Add a function

You can always add more design functions to a Mechanic project. To do so, run the following command in the terminal in your mechanic project folder. This will create a new folder in your Mechanic project with an index.js file based on a template or an example.

npm run new

Alternatively, you can create the corresponding folders and files manually to add a new design function. Make sure you follow a similar folder structure as the one mentioned and that you add the necessary dependencies to you project to make it work.

Add an input

inputs is an object defining the variables that will be passed to your design function. Each input will create an element in the design tool UI, and users can change these inputs and run the function with updated values. There are many types of inputs and each of them have different settings that change their behavior. Once defined, the input will be accessible in the handler function via the name provided in the inputs object.

Definition
const inputs = {
  name: {
    type: input_type,
    default: value,
    validation: validation_function,
    editable: editable_function ,
    label: label_string
  }
};
Use in handler
export const handler = ({ inputs, mechanic }) => {
  const { name } = inputs;
  // Use "name" in your code...
};

The following properties are shared among all inputs.

  • type: string value that determines the type of variable ("number", "text", "color", "boolean", "image"). It determines the type of object that the handler will receive as a value and how it looks in the UI. This property is required.
  • default: value that acts as a fallback for input if not yet set by the user. This property is required for most types of inputs, except for "boolean" and "image".
  • validation: function that receives the current value assigned to the input and outputs an error (as a string) when the value entered doesn't pass a certain condition. When the given function returns an error string, the error is shown in the UI. If the value passes conditions (no error) a null value is expected.
  • editable: function that receives all current values assigned to the defined inputs and determines if the input can be edited or not. A boolean value is expected as an output of the given function.
  • label: string value to use as label for input in UI input. If not set, by default the input name is used as the label for the UI.
  • Text

    The text input lets you define a variable for text values. This will be translated in a UI input that accepts text and the function handler will receive a string value.

    myText: { 
     type: "text", 
     default: "hello", 
    }
  • Number

    The number input lets you define a variable for numeric values. This will be translated into a UI input that accepts numbers and the function handler will receive a number value.

    myNumber: { 
     type: "number", 
     default: 10,
    }
  • Color

    The color input lets you define a variable for color values. This will be translated into a color selector UI input and the handler will receive a string value that represents a color. By default, the color model expected is rbga but it can be changed with a property to hex.

    myColor: { 
     type: "color", 
     model: "hex", 
     default: "#ff00ff" 
    }
  • Boolean

    The boolean input lets you define a variable for a boolean value (either true or false). This input will be translated into a toggle button element that changes the value, and the handler will get the corresponding boolean value.

    myBoolean: { 
     type: "boolean", 
     default: true, 
    }
  • Image

    The image input lets you upload an image. The UI will show an input that can be clicked to upload an image from your computer. The multiple property allows for upload of multiple images in the same input. The handler function will receive a File object or a FileList object depending on the number of images.

    myImage: { 
     type: "image",
     multiple : true 
    }
  • Slider

    A number input displayed as a slider. To do so, you need to set slider attribute to "true" and set a minimum (min) and maximum (max) value which defined the range of the slides. You can also set a step value that will define how much the value changes when you move the slider.

    mySlider: { 
     type: "number", 
     min: 100, 
     max: 500, 
     step: 5, 
     slider: true, 
     default: 400 
    }
  • Select

    Number, text, and color inputs can be displayed as a dropdown input from which you can pick one option. To do so, set the options property which expects an array of primitives or objects.

    mySelect: { 
     type: "number", 
     default: 10, 
     options: [10, 13, 30] 
    }

Add a preset

Presets give you the opportunity to set predefined sets of values for the inputs that users can select with a single click in the design tool UI. You can have multiple presets per function, which allow the user to quickly select a number of values in a single action.

export const presets = {
  presetName1: {
    input1: value,
    input2: value,
    // ...
  },
  presetName2: {
    input1: value,
    input2: value,
    // ...
  },
};

Handler arguments

The handler function of a design function always receives a single argument with at least two properties that are key to the function's rendering: inputs and mechanic. Depending on the engine, the handler function may receive more properties in the object argument. Read more about the individual engines.

  • inputsThe inputs property will contain all values selected through the UI interface for each defined input. Your design function can use these values to change what is being rendered.
  • mechanicThe mechanic property serves as a proxy to Mechanic’s core functionality, wrapping two important methods: mechanic.done() and mechanic.frame(). The done() method tells Mechanic that the code to generate an asset has finished. This function must be called at some point in the handler's code for both static and animated assets. For animated assets, the frame() method adds separate frames of the asset and is expected to be called before the call to done().

Select an engine

Engines are JavaScript functions that enable certain web technologies with Mechanic’s environment. Currently there are four official engines published. It is unlikely you will need to write an engine from scratch, but the offical engines should be a good guide in case you want to enable a non-supported JavaScript library to work with Mechanic.

Canvas

This engine enables the use of the Canvas API in the Mechanic framework. While using this engine, both the frame() and the done() method expect to be called with a single argument: the canvas object that the design function is using to render the asset.

Definition
const settings = {
  engine: require("@mechanic-design/engine-canvas"),
};
Use in handler
export const handler = ({ inputs, mechanic }) => {
  const { width, height, color } = inputs;
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const ctx = canvas.getContext("2d");
  ctx.fillStyle = color;
  // ...
  mechanic.done(canvas)
};

SVG

This engine enables the use of SVG strings in the Mechanic framework. While using this engine, both methods of the mechanic handler parameter are expected to be called with a single argument: the string value of the SVG image.

Definition
const settings = {
  engine: require("@mechanic-design/engine-svg"),
};
Use in handler
export const handler = ({ inputs, mechanic }) => {
  const { width, height, color } = inputs;
  const svg = `<svg width="${width}" height="${height}">
      <rect fill="${color}" width="${width}" height="${height}" />
    </svg>`

  mechanic.done(svg);
};

p5.js

This engine enables the use of p5.js in the Mechanic framework.

Definition
const settings = {
  engine: require("@mechanic-design/engine-p5"),
};
Use in handler
export const handler = ({ inputs, mechanic, sketch }) => {
  const { width, height, color } = inputs;
  sketch.setup = () => {
    sketch.createCanvas(width, height);
  };

  sketch.draw = () => {
    sketch.background(color);
    mechanic.done();
  };
};

React

This engine enables the use of the React SVG components in the Mechanic framework.

Definition
const settings = {
  engine: require("@mechanic-design/engine-react"),
};
Use in handler
export const handler = ({ inputs, mechanic }) => {
  const { width, height, color } = inputs;
  useEffect(() => {
    mechanic.done();
  }, []);

  return (
    <svg width={width} height={height}>
      <rect fill={color} width={width} height={height} />
    </svg>
  );
};

Settings

Settings allow you to set general configuration for your design function. The following are the possible properties that are available to be set.

  • engine: sets the engine for the design function. This property always has to be set.
  • name: Set the display name for the design function in the UI.
  • animated: determines whether the design function is an animated sequence or a static image. Defaults to false.
  • usesRandom: if true, enables a seeded random that forces the export to generate the same output as the last preview. Defaults to false.
  • optimize: optimizes SVG outputs using SVGO. If an object is received, it is merged with the default SVGO options and passed to the optimize function. Defaults to true.

Finally

If you want to dive deeper into Mechanic we suggest checking out the Github page.

We are very interested in hearing feedback from you, so please let us know what you think.