Generative vector artwork in Node.js with Paper.js using a particle simulation.
particle-vector-art.stuffjackmakes.com has a running demo of the web interface. This interface was developed as a visual aid in finding interesting run property configurations to use in the (quicker) Node.js application, and as such the UI is minimal.
Clone the repository to your local machine and install the required libraries with:
npm installThen run the program with the provided run-properties.json and heightmap.png with:
node index.jsAnd the output image(s) will be saved to the output folder, along with corresponding json metadata files in the output/metadata folder. Both folders are generated at runtime.
To use different heightmaps, move the desired images to the heightmaps folder. The program detects all images in that folder and runs a simulation against each, repeating as defined in the run properties.
To use different run properties, edit run-properties.json and run the program. All possible run property values are described in the Run Properties Format section below.
Everything required to run this in the browser is included in the public folder. To host the contents of that folder, run:
cd public/
npx http-serverThen go to http://localhost:8080 in your web browser.
To bundle the code using Browserify and copy run-properties.json to the public folder, run:
npm run webThe run-properties.json file controls the parameters of the simulation. All values are optional and revert to their default value if none is provided. The run-properties-all.json contains all possible options as an example for how each is set.
repeatTimes: How many additional times to generate an image for each heightmap. Note that this value does nothing in the browser. Default value is 0.width: Width of the generated image. Does not need to be related to the heightmap dimensions, and is itself dimensionless since the generated image is a vector. Default value is the width of the provided heightmap.height: Width of the generated image. Does not need to be related to the heightmap dimensions, and is itself dimensionless since the generated image is a vector. Default value is the width of the provided heightmap.numParticles: How many particles to simulate. Default value is a random integer between 1 and 8.totalSteps: How many times the simulation will last. Default value is a random integer between 100 and 500.allowBackgroundColor: iftruethen particles can be the same color as the background. Default value is randomly 50% true and 50% false.palette: A list of the colors to choose from, as hex strings (e.x. ["#F9A799", "#B7CBBF"]). If missing, a random palette from code/color-palettes.json will be chosen, or ifrandom.brandNewPaletteChanceis non-zero, a completely random palette could be generated.backgroundColor: The color to be drawn as the background, as a hex string (e.x. "#F9A799"). If missing, a random color from the palette will be chosen.
random.nudgePropertiesChance: The chance (0.0 to 1.0) to modify each of the run property values (this will not modify the original file). Useful for generating lookalike permutations. Default value is 0.random.nudgePropertiesMagnitude: The amount (0.0 to 1.0) to modify 'nudged' run property values. 1.0 indicates that the property could be anywhere from zero to double its initial value. Default value is 0.2.random.brandNewPaletteChance: The chance (0.0 to 1.0) that an undefined palette is made up of completely random colors versus using a palette from the code/color-palettes.json file. Default value is 0.
utilityProperties.noiseType: The type of noise used in randomization functions as enumerated in code/constants.js. Default value is 0 for simplex noise.utilityProperties.easingFunction: The easing functions used when transitioning between control functions over time. See code/utilities.js for enumeration and easings.net for details. Default value is 0 for linear easing.utilityProperties.xNoiseScale: How noise output should be scaled along the X axis. A range of roughly0.0025to0.1works well, depending on preference for smooth or quick transitions in the noise field. This should also be sized in relation towidth. Default value is 0.015.utilityProperties.yNoiseScale: How noise output should be scaled along the Y axis. A range of roughly0.0025to0.1works well, depending on preference for smooth or quick transitions in the noise field. This should also be sized in relation toheight. Default value is 0.015.utilityProperties.noiseTimeScale: How noise output should be scaled as the simulation progresses. A low value (e.x.0.0001means that the noise field doesn't change much over the course of the iteration. A high value (e.x.0.1) means that the noise field changes more quickly. Default value is 0.001.utilityProperties.noiseFieldScale: The factor by which to 'chunk' out noise values, lowering the variance for nearby 'chunks' while increasing it for adjacent 'chunks'. A value of50means that the noise field is split into a grid of 50x50 (dimensionless) squares, where the noise value in each square will always be the same value. A value of1means that this field has no effect. Default value is 1.utilityProperties.seed: A seed for the Simplex and Brownian noise functions. Note that this does not indicate reproducability, as Javascript's built-inMath.random()does not accept a seed input. Default value isDate.now().
particleDefaults.minSize: The minimum size a particle can be. This should be sized in relation towidth. Default value is 1.particleDefaults.maxSize: The maximun size a particle can be. This should be sized in relation toheight. Default value isMath.sqrt(width * height) / 32.particleDefaults.minLifespan: The minimum number of iterations a particle operates for before it is replaced. This should be sized in relation tototalSteps. Default value istotalSteps / 100.particleDefaults.maxLifespan: The maximum number of iterations a particle operates for before it is replaced. This should be sized in relation tototalSteps. Default value istotalSteps / 2.particleDefaults.minSpeed: The minimum distance a particle must move in a single iteration. This should be sized in relation towidthandheight. Default value is 1.particleDefaults.maxSpeed: The maximum distance a particle must move in a single iteration. This should be sized in relation towidthandheight. Default value isMath.sqrt(width * height) / 50.particleDefaults.skipPattern: Eithernullfor no skip pattern, or an array of integers indicating a sequence of iterations that the particle should or should not be drawn for. For example[3,2,5,4]would indicate that the particle should be drawn the first three iterations, not drawn the next two, drawn the next five, not drawn the next four, then repeat the cycle. Default value is a 50% chance ofnulland a 25% change of a randomly-generated skip pattern.particleDefaults.lineCapBehavior: How particle trails are shaped at the ends as enumerated in code/constants.js. See Paper.js documentation for details about the specific behavior of each setting. Default value is a random integer between 0 and 2 (random line cap behavior).particleDefaults.lineJoinBehavior: How particle trails are shaped at the intersection between iterations as enumerated in code/constants.js. See Paper.js documentation for details about the specific behavior of each setting. DEault value is a random integer between 0 and 2 (random line join behavior).particleDefaults.edgeBehavior: What happens to particles when they reach the edge of the drawing area. See code/constants.js for enumeration and details. Deault value is a random integer between 0 and 4 (random edge behavior).particleDefaults.overshoot: How much further to draw a particle's path beyond what it moved in the last iteration (dimensionless). Default value is 0 80% of the time and a random number between 1 and 10 the rest of the time.
positionController.noiseOffset: A non-zero multiplicative offset for noise calculations used so that this controller's noise values are different from other controllers. Each controller with the same offset will have the same noise values for a given position and iteration. Default value is 1.positionController.controlFunction: The name of the function that will be used to control particle start positions. Default value is a random control function. Valid values are:Center: Particles start at the center of the canvas.Point: Particles start at a point defined bypositionController.fixedPointXandpositionController.fixedPointY.Noise: Particles start at a point on the canvas based onutilityProperties.noiseTypenoise.Spiral: Particles start spiraling out from the center in a spiral pattern based onpositionController.angleStart,positionController.radiusScale,positionController.spiralAngle, andpositionController.spiralCount.TimeMux: Particles start in a position defined bypositionController.controlMuxAand over the course of the simulation shift to start in a position defined bypositionController.controlMuxAusingutilityProperties.easingFunctionto ease between values defined by each control function.
positionController.controlMuxA: The control function to ease from when mux-ing. Can be any of thepositionController.controlFunctionvalues exceptTimeMux. Default vaue is a random control function.positionController.controlMuxB: The control function to ease to when mux-ing. Can be any of thepositionController.controlFunctionvalues exceptTimeMux. Default vaue is a random control function.positionController.radiusScale: How far to move outwards from the center when creating each new particle for theSpiralcontrol function. This should be sized in relation towidth. Default value is a random number between 5 and min(width,height).positionController.spiralAngle: How many degrees to rotate when creating each new particle for theSpiralcontrol function. A value of zero results in a straight line, and a value of 137.5 results in a phyllotaxis pattern. Default value is a random number between 1 and 180.positionController.angleStart: The starting angle (degrees) for theSpiralcontrol function. Default value is a random number between 0 and 360.positionController.fixedPointX: The x coordinate for thePointcontrol function. Default value is a random number between 0 andwidth.positionController.fixedPointY: The y coordinate for thePointcontrol function. Default value is a random number between 0 andheight.
angleController.noiseOffset: A non-zero multiplicative offset for noise calculations used so that this controller's noise values are different from other controllers. Each controller with the same offset will have the same noise values for a given position and iteration. Default value is 2.angleController.heightmapChannel: Which color channel of the heightmap to react to, as enumerated in code/constants.js. Default value is the RGB channel.angleController.controlFunction: The name of the function that will be used to control particle acceleration angles at each iteration. Default value is a random control function. Valid values are:Noise: Particle acceleration vectors are based onutilityProperties.noiseTypenoise.Random: Particle acceleration vectors are random each iteration.Constant: Particle acceleration vectors are constant.GlobalConstant: Particle acceleration vectors are all the same constant as defined inangleController.globalConstantAngleXandangleController.globalConstantAngleY.Curls: Particle acceleration vectors aim paritlces to makeangleController.curlCountcurls.Waves: Particle acceleration vectors aim particles to makeangleController.waveCountsinusoidal waves.Loops: Particle acceleration vectors aim particles to makeangleController.loopCountclockwise loops.Heightmap: Particle acceleration vectors aim in opposite directions at each extreme of the heightmap, depending on thecolorController.heightmapChannelvalue at the particle's positionTimeMux: Particles start with an acceleration vector defined bycolorController.controlMuxAand over the course of the simulation shift to an acceleration vector defined bycolorController.controlMuxAusingutilityProperties.easingFunctionto ease between values defined by each control function.HeightmapMux: Particles ease between the acceleration vector defined bycolorController.controlMuxAand the acceleration vector defined bycolorController.controlMuxAusingutilityProperties.easingFunctionto ease between values, depending on thecolorController.heightmapChannelvalue at the particle's position.
angleController.controlMuxA: The control function ease from when mux-ing. Can be any of theangleController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.angleController.controlMuxB: The control function ease to when mux-ing. Can be any of theangleController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.angleController.globalConstantAngleX: The x component of the acceleration vector for theGlobalConstantcontrol function. Resulting magnitude does not matter as it is normalized. Default value is a random number from -1 to 1.angleController.globalConstantAngleY: The y component of the acceleration vector for theGlobalConstantcontrol function. Resulting magnitude does not matter as it is normalized. Default value is a random number from -1 to 1.angleController.roundAngleResults: A boolean (e.g. true or false) that, if true, rounds acceleration vector output to the nearestangleController.roundToAngle. Default value is randomly 50% true and 50% false.angleController.roundToAngle: The angle (in degrees) to round acceleration vectors to ifangleController.roundToAngleis true. Default value is an integer between 10 and 90;angleController.curlCount: How many curls the acceleration vector aims particles into for theCurlscontrol function. Defaults to a random integer between 1 and 10.angleController.waveCount: How many waves the acceleration vector aims particles into for theWavescontrol function. Defaults to a random integer between 1 and 10.angleController.loopCount: How many loops the acceleration vector aims particles into for theLoopscontrol function. Defaults to a random integer between 1 and 10.
colorController.noiseOffset: A non-zero multiplicative offset for noise calculations used so that this controller's noise values are different from other controllers. Each controller with the same offset will have the same noise values for a given position and iteration. Default value is 3.colorController.heightmapChannel: Which color channel of the heightmap to react to, as enumerated in code/constants.js. Default value is the RGB channel.colorController.controlFunction: The name of the function that will be used to control particle acceleration angles at each iteration. Default value is a random control function. Valid values are:Noise: Particles have a color based onutilityProperties.noiseTypenoise.Random: Particles have a completely random color every iteration.RandomPalette: Particles have a random color from the palette every iteration.Constant: Particles have a constant base color.GlobalConstant: All particles have the same base color as defined incolorController.globalConstantColor.Inverse: Particle have the inverse of whatever their base color would be.FadeOut: Particles fade from their base color to the background color over their lifespan.FadeIn: Particles fade from the background color to their nase color over their lifespan.FadeInOut: Particles fade from their base color to the background color repeatedlycolorController.fadeCounttimes over their lifespan.TimeMux: Particles start with a color defined bycolorController.controlMuxAand over the course of the simulation shift to a color defined bycolorController.controlMuxAusingutilityProperties.easingFunctionto ease between values defined by each control function.HeightmapMux: Particles ease between the color defined bycolorController.controlMuxAand the color defined bycolorController.controlMuxAusingutilityProperties.easingFunctionto ease between values, depending on thecolorController.heightmapChannelat the particle's position.
colorController.controlMuxA: The control function ease from when mux-ing. Can be any of thecolorController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.colorController.controlMuxB: The control function ease to when mux-ing. Can be any of thecolorController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.colorController.globalConstantColor: The color of particles for theGlobalConstantcontrol function, as a hex string (e.x. "#b165f3"). Default value is a random color from the current run's palette.colorController.fadeCount: How many times the color of particles for theFadeInOutcontrol function will fade. Default value is a random integer between 1 and 8.
magnitudeController.noiseOffset: A non-zero multiplicative offset for noise calculations used so that this controller's noise values are different from other controllers. Each controller with the same offset will have the same noise values for a given position and iteration. Default value is 4.magnitudeController.heightmapChannel: Which color channel of the heightmap to react to, as enumerated in code/constants.js. Default value is the RGB channel.magnitudeController.controlFunction: The name of the function that will be used to control the magnitude of each particle's acceleration at each iteration. Default value is a random control function. Valid values are:Noise: Particles have an acceleration magnitude based onutilityProperties.noiseTypenoise.Random: Particles have a random acceleration magnitude every iteration.Constant: Particles have a constant acceleration magnitude.GlobalConstant: Particles all have the same constant acceleration magnitude.Speedup: Particles increase their acceleration magnitude fromparticleDefaults.minSpeedtoparticleDefaults.maxSpeedover their lifespan.Slowdown: Particles decrease their acceleration magnitude fromparticleDefaults.maxSpeedtoparticleDefaults.minSpeedover their lifespan.Boosts: Particle acceleration magnitudes vary fromparticleDefaults.minSpeedtoparticleDefaults overangleController.waveCount` sinusoidal waves.Heightmap: Particle acceleration magnitude ranges fromparticleDefaults.minSpeedtoparticleDefaults.maxSpeeddepending on themagnitudeController.heightmapChannelvalue at the particle's position.TimeMux: Particles start with an acceleration magnitude defined bymagnitudeController.controlMuxAand over the course of the simulation shift to an acceleration magnitude defined bymagnitudeController.controlMuxAusingutilityProperties.easingFunctionto ease between values defined by each control function.HeightmapMux: Particles ease between the acceleration magnitude defined bymagnitudeController.controlMuxAand the acceleration magnitude defined bymagnitudeController.controlMuxAusingutilityProperties.easingFunctionto ease between values, depending on themagnitudeController.heightmapChannelvalue at the particle's position.
magnitudeController.controlMuxA: The control function ease from when mux-ing. Can be any of themagnitudeController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.magnitudeController.controlMuxB: The control function ease to when mux-ing. Can be any of themagnitudeController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.magnitudeController.globalConstantColor: The acceleration magnitude of particles for theGlobalConstantcontrol function. Default value is a random number between 0 and 1. 0 in this case translates toparticleDefaults.minSpeedand 1 translates toparticleDefaults.maxSpeed.angleController.boostCount: How many boosts the acceleration magnitude experiences into for theBoostscontrol function. Defaults to a random integer between 1 and 10.
sizeController.noiseOffset: A non-zero multiplicative offset for noise calculations used so that this controller's noise values are different from other controllers. Each controller with the same offset will have the same noise values for a given position and iteration. Default value is 5.sizeController.heightmapChannel: Which color channel of the heightmap to react to, as enumerated in code/constants.js. Default value is the RGB channel.sizeController.controlFunction: The name of the function that will be used to control the size of each particle at each iteration. Default value is a random control function. Valid values are:Noise: Particles are a size based onutilityProperties.noiseTypenoise.Random: Particles are a random size every iteration.Big: Particles are alwaysparticleDefaults.maxSize. Useful for 'mux' control functions.Medium: Particles are always halfway betweenparticleDefaults.minSizeandparticleDefaults.maxSize. Useful for 'mux' control functions.Small: Particles are alwaysparticleDefaults.minSize. Useful for 'mux' control functions.Shrink: Particles start atparticleDefaults.maxSizesize and shrink toparticleDefaults.minSizeover their lifetime.Grow: Particles start atparticleDefaults.minSizesize and grow toparticleDefaults.maxSizeover their lifetime.Heightmap: Particle size ranges fromparticleDefaults.minSizetoparticleDefaults.maxSizedepending on themagnitudeController.heightmapChannelvalue at the particle's position.TimeMux: Particles start with a size defined bymagnitudeController.controlMuxAand over the course of the simulation shift to a size defined bymagnitudeController.controlMuxAusingutilityProperties.easingFunctionto ease between values defined by each control function.HeightmapMux: Particles ease between the size defined bymagnitudeController.controlMuxAand the size defined bymagnitudeController.controlMuxAusingutilityProperties.easingFunctionto ease between values, depending on themagnitudeController.heightmapChannelvalue at the particle's position.
sizeController.controlMuxA: The control function ease from when mux-ing. Can be any of thesizeController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.sizeController.controlMuxB: The control function ease to when mux-ing. Can be any of thesizeController.controlFunctionvalues exceptTimeMuxandHeightmapMux. Default value is a random control function.
- Expand upon "rounding" from Angle controller (e.x. limit the size controller sizes to 2, 4, 6, 8, etc)
- Add symmetry, where particles are reflected across specific axes
- Add particle interation (e.g. gravity or flocking)
- Zig-zag angle controller
- Color Controller PaletteNoise control funcion, where noise controls fading between colors in the palette (vs all colors)
- Mode where particle defaults depend on their base color (i.e. all particles of the same color act in a similar fashion)
- Custom line ends (e.g. a jagged paintbrush look)
- Allow mutliple heightmaps
- Control functions that operate off of particle properties (e.g. velocity, position, etc)
Check out my other work at stuffjackmakes.com