3.6.1 • Published 1 year ago

@hiberworld/code-kit-express v3.6.1

Weekly downloads
-
License
UNLICENSED
Repository
github
Last release
1 year ago

Hiber Engine: Code-Kit Express

Code-Kit is the scripting interface for the Hiber Engine API, and Code-Kit _Express is an additional layer built on top of it.

Code-Kit Express includes a set of functions called helpers, which provides a simplified interface and hide the complexity of working directly with the underlying layer.

Please see Code-Kit for more information.

Hiber Engine is a engine specifically designed for the Metaverse. It is built using standard web technology, including C++ converted to Web Assembly, and adds features such as multiplayer, interconnections between virtual worlds, interoperability, and support for web3 technologies. As it is built using standard web technology, there is no need to download any additional software. Hiber Engine creates a layer for the Metaverse on top of the existing internet.

Hiber Engine is the engine used on the web site www.hiberworld.com.

Setup

Dependencies

installation

Run yarn to install dependencies

Local development

Run yarn dev to start a local development server

Keyboard shortcuts

V - Toggle fly mode

L - Open list of assets and materials

M - Open game menu

IDE

What code editor to use is up to you but we recommend VS Code from MicroSoft, it's free and widely used. CodeKit Express is developed in TypeScript also from MicroSoft. Most properties are typed and have auto completion. In most IDE´s you access it with Ctrl+Space.

Auto Completion

Getting started

To start working with CodeKit Express create a new project in the project folder src/projects. The easiest way to do this is to duplicate the project empty.ts. Make sure to change name from empty in the file to the name of your project, for example testProject.

You should now have a project file that looks like this:

import { addTo, draw, getGround, getRoot } from '../helpers';

export const testProject = () => {
  // Create the root object
  const root = getRoot();

  // Add something to stand on
  const ground = getGround({ hilly: 0.1 });
  addTo(root, ground);

  // Draw the scene
  return draw(root);
};

To run this project you must add it to src/index.ts like this.

import { CodeKit } from '@hiberworld/code-kit';
import { addKeyboardShortcuts } from './helpers';
import { onHotReload } from './helpers/onHotReload';
import { testProject } from './projects/testProject';

if (import.meta.hot) {
  onHotReload();
  import.meta.hot.accept();
}

const scene = testProject(); // <-- Add project here

const hs = new CodeKit();
addKeyboardShortcuts(hs);
hs.renderScene('canvas', scene);

The scene should look like this:

empty scene

Naming

CodeKit Express consists of a set of functions called helpers. Most of these helpers create and return something or adds something to an existing object.

get-helpers

Helpers that create and returns something starts with get-, like getPrefab. An object returned from a get-helper will only be visible in the scene if it's added to another parent object. This is done using the helper addTo.

const cube = getPrefab({ p: [0, 0, 5] });
addTo(root, cube);

add-helpers

Helpers that adds something starts with add-, like addPointLight. An add-helper needs a target object to work as it should. This target helper is also returned.

const lightSource = getGroup({ p: [0, 2, 5] });
addPointLight(lightSource);
addTo(root, lightSource);

Adding a 3D object to the scene

3D objects in HiberEngine are called Prefabs. A prefab can contain a number of 3D objects, animations. particles etc. The most simple way to add a prefab to the scene is by using the helper getPrefab:

const cube = getPrefab();
addTo(root, cube);

or shorter

addTo(root, getPrefab());

Add this to your project file and a cube will appear on the scene.

import { addTo, draw, getGround, getPrefab, getRoot } from '../helpers';

export const testProject = () => {
  // Create the root object
  const root = getRoot();

  // Add something to stand on
  const ground = getGround({ hilly: 0.1 });
  addTo(root, ground);

  const cube = getPrefab(); // <-- your code here
  addTo(root, cube);

  // Draw the scene
  return draw(root);
};

Box on scene

This code places a cube at the location of the avatar in the center of the scene. However, you may want to customize the cube further. Let's explore how to modify the code to add additional properties to the cube.

const cube = getPrefab({
  id: 'cube_01', // What prefab to use
  p: [0, 0, 5], // Position, default is 0, 0, 0
  r: [0, 0, 0], // Rotation in Euler/degrees, default is 0, 0, 0
  s: [1, 1, 1], // Scale, default is 1, 1, 1
});

In the code above we define what prefab to use and sets its position, rotation and scale.

cube positioned

If we would like to add anything else but a cube we change the line:

id: 'cube_01',

To something else, like this:

id: 'wooden_wheel_01',

This will add a wooden wheel the the scene. Lets rotate and scale it to see how it works.

const wheel = getPrefab({
  id: 'wooden_wheel_01',
  p: [0, 0, 5],
  r: [0, -30, 0],
  s: [4, 4, 4],
});

wooden_wheel

Materials

Changing material on a prefab is easy. Just add the material property. To see available materials hit Ctrl+Space after the colon in material:.

const wheel = getPrefab({
  id: 'wooden_wheel_01',
  p: [0, 0, 5],
  r: [0, -30, 0],
  s: [4, 4, 4],
  material: 't_rainbow_02',
});

material_wheel

Assets and materials

To see what assets and materials press L to open the Assets browser. It will open a new tab in your browser.

  • Click on the thumbnails to copy the assets id
  • Double click to copy is as prefab code
  • Click the Change to Material button to the right top to see available materials

npm.io

Ground

Unless you want to create a space or water world a ground can be handy. There's a helper for that called getGround. You have seen it being used in the examples above.

In it's most basic form it looks like this:

addTo(root, getGround());

flat_ground

This will add a flat grass plane. You can define how hilly it should be with the property hilly.

const ground = getGround({ hilly: 10 });
addTo(root, ground);

hilly

Material

To change the material of the ground, similar to how it is done with a prefab, you can use the material property.

const ground = getGround({ hilly: 0, material: 't_stone_floor_01' });

Size

The size of the ground can be changes in two ways. The easiest way is to just change the scale.

const ground = getGround({ hilly: 1, s: pop3(3) });

Tips: Use pop3 to populate a vector3 with the same number.

Another way is to change the number of ground blocks being used by changing repeatX and repeatZ from it's default value 4. You can of course also use both repeatX , repeatZ and scale.

const ground = getGround({ hilly: 1, repeatX: 1, repeatZ: 10 });

A cave can easily be created using two grounds.

addTo(root, getGround({ hilly: 1, material: 't_sand_01' }));
addTo(root, getGround({ hilly: 10, material: 't_rock_03', r: [180, 0, 0], s: [1, 4, 1] }));

cave

Images

You add an image to an object like this:

const cube = getPrefab({ p: [0, 0, 6] });
addImage(cube, 'https://cdn.hibervr.com/demo/img/Mona.jpg');
addTo(root, cube);

addImage

Images for testing can be found here: src/assets/artData.ts

Poster

If you want to add an image like a flat surface, like a poster, you can use the helper getImage:

const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addTo(root, img);

getImage

Framed image

You can also add an image with a frame by using getFramedImage.

frame property equals: 0 = Golden frame 1 = Stone frame 2 = Modern frame -1 = random

const img = getFramedImage({
  src: 'https://cdn.hibervr.com/demo/img/Mona.jpg',
  ratio: 671 / 1000,
  frame: 0,
  p: [0, 1, 0],
  s: pop3(1),
});
addTo(root, img);

getFramedImage

Video

You add a video to an object using addVideo.

const cube = getPrefab({ p: [0, 0, 6] });
addVideo(cube, { src: 'https://cdn.hibervr.com/video/bike.mp4' });
addTo(root, cube);

addVideo

Here are some videos to try out:

  • bike
  • brand
  • dj
  • Cubes_Waves
  • Kaleidoscope
  • Tunnel_Loop
  • Cubes_Loop

VideoPlayer

You can also add a video using the helper getVideoPlayer. This adds an video to a flat surface.

addTo(root, getVideoPlayer({ src: 'https://cdn.hibervr.com/video/bike.mp4', p: [0, 0, 5] }));

Interactive Media Asset

Interactive Media Asset (IMA) could be described as links to other sites. Say you have an image of an product and would like to make it possible to open up a wab page with more information about the product. Then you should use an IMA.

Any object, not group or node, could be turned into a IMA. You add it using addInteractiveMediaAsset.

const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addInteractiveMediaAsset(img, {
  header: 'Mona Lisa',
  body: 'Also called Portrait of Lisa Hibertini',
  url: 'https://hiberworld.com/',
});
addTo(root, img);

IMA

Portal

PORTAL NEEDS MORE LOVE TO WORK 100%

If you instead want to link to another world on www.HiberWorld.com you use portals. To add a portal you use the helper getPortal.

Find LevelID in the url of a world. Ex: https://hiber.hiberworld.com/?Launch.InitLevelID=1237022115291292

const prt = getPortal({ LevelID: 1237022115291292, p: [0, 0, 10] });
addTo(root, prt);

Clone

If you want another prefab with the same properties you can use the clone function, getClone.

const wheel2 = getClone(wheel, { p: [-2, 3, 8], r: [-22, 33, 44] });

This code will create a clone of the prefab. If you provide new values for the properties of the prefab as arguments, they will override the corresponding values in the cloned prefab.

getClone

Symmetrical clone

Say you want to build something symmetrical like a spaceship or a vehicle. Then the getMirrorX, getMirrorY and getMirrorZ is handy. These functions are a special set of the cone function. They clone an item and then flip the rotation and position to make it look like a mirrored object.

Say you hav wedge placed next to a scaled box, and you want to create a mirror of it on the other side.

const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, wing);

hull

It's done like this using getMirrorX:

const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, [wing, getMirrorX(wing)]);

getMirrorX

Note: It's not perfect since there's no distortion happening, just rotation and repositioning.

Groups

You can also group prefabs together. This is powerful if you want to rotate, scale, move or clone and entire group of objects.

Create a group using getGroup:

let grp = getGroup({p: [0, 0, 5]});

This will place a group 5 meters (1 meter = 3.28 feet) in front of you.

let wheelGrp = getGroup({ p: [0, 0, 5] });
addTo(root, wheelGrp);

const wheel = getPrefab({
  name: 'wheel',
  id: 'wooden_wheel_01',
  p: [0, 0, 0], // <-- Change to 0, 0, 0 to place it in the center of the group
  r: [0, -30, 0],
  s: [4, 4, 4],
  material: 't_rainbow_02',
});
addTo(wheelGrp, wheel);

The tentacle

Not just groups can contain other objects, in fact any object can have children. With a few lines of code you can get this:

the tentacle

let parent = addTo(root, getPrefab({ id: 'color_cube_02_04', p: [0, 0, 20], r: [0, 0, -20] }));
for (let i = 0; i < 20; i++) parent = addTo(parent, getClone(parent, { p: [0, 2.2, 0], r: [0, 15, 15], s: pop3(0.9) }));

Animations

CodeKit Express has a number of ways to animate/tween objects.

Tween using key frames

Tween an object using key frames using the helper getTween.

Note: p, r, and s defined in the key frames is relative values to the objects position, rotation and scale.

// Create key frames, ts = timeStamp 0 - 1
const kf: KeyFrameOptions[] = [
  { ts: 0.25, p: [0, 2, 0], easing: 'LINEAR' }, // <-- default easing is EASE_IN_OUT_QUAD
  { ts: 0.5, p: [2, 2, 0] },
  { ts: 0.5, r: [0, 90, 180] },
  { ts: 0.75, p: [2, 0, 0] },
  { ts: 0.8, r: [0, 90, 0] },
  { ts: 1, p: [0, 0, 0] },
];

// Create a wrapper for the tweens, use this to place, rotate and scale the animated object.
const tweenWrapper = getGroup({ p: [0, 1, 5] });
addTo(root, tweenWrapper);

// Create object to tween and apply the tween
const spr = getPrefab({ id: 'sphere_01' });
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'RESTART' }));

kf-tween

Tween options

  • loopBehaviour - RESTART | REVERSE, default: REVERSE
  • duration - The length of the tween, default: 2
  • progress - Where in the tween should it start. Ex 0.5 starts 50% in, default: 0
  • speedMultiplier - Change the speed, default: 1

Spin

If you want a more basic animation like making an object spin. Heres the basic code for that.

// First create an spinning group
const spinner = getSpinningGroup({ p: [0, 0, 10], axis: 'y', r: [10, 20, 30] });
addTo(root, spinner);
// Create what should be spinning and add it to the group.
const cube = getPrefab();
addTo(spinner, cube);

You can get the same result by using the helper getSpinning.

const cube = getPrefab();
addTo(root, getSpinning(cube, { p: [0, 0, 10], axis: 'y', r: [10, 20, 30] }));

Note: The cube is wrapped in an Animated Node. You must add the object returned from getSpinning to the scene to get the cube spinning.

getSpinning

Move

The get an object to move you can use key frames like this.

const kf: KeyFrameOptions[] = [
  { ts: 0, p: [0, 0, 0] },
  { ts: 1, p: [0, 4, 0] },
];

const tweenWrapper = getGroup({ p: [0, 0, 10], r: [0, 30, 20] });
addTo(root, tweenWrapper);

const spr = getPrefab();
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'REVERSE' }));

You can get the same result by using the helper getMoving.

const cube = getPrefab();
addTo(root, getMoving(cube, { to: [0, 4, 0], p: [0, 0, 10], r: [0, 30, 20] }));

Note: to is where it should move to relative to tho objects position p.

getMoving

Environment

Change environment by adding an environment id to the draw function. To see available environment hit Ctrl+Space after return draw(root, '.

return draw(root, 'starry_night_01');

environment

Lights

HiberEngine has two kind of lights. A light source is added to an object or group.

Note: Lights are heavy to render, don't add to many and don't add many close to each other.

PointLight

A point light can be added using addPointLight.

const pl = addPointLight(getGroup({ p: [0, 0, 6] }));
addTo(root, pl);

Or use the helper getPointLight

addTo(root, getPointLight({ p: [0, 1, 5] }));

getPointLight

Spotlight

Spotlight can be added in a similar way using addSpotlight.

const sl = addSpotlight(getGroup({ p: [0, 2, 6] }));
addTo(root, sl);

Or use the helper getSpotlight

addTo(root, getSpotlight({ p: [0, 2, 6] }));

getSpotlight

Tips: To get a random color use the helper getRandomColor

addTo(root, getSpotlight({ p: [0, 2, 6], color: getRandomColor() }));

Sound

You can add a sound source to an object using addAudio.

const cube = getPrefab({ p: [0, 0, 5] });
addAudio(cube, { id: 'a_m_iconic_bullet_01' });
addTo(root, cube);

id is the id of the sound file in the Hiber assets database. As for now you can only use sounds from there.

There are various properties that can be used to make the sound behave differently. To make things easier, there are three helpers available instead of trying out the properties individually.

Point Sound

Add a point sound to a object using addPointSound. A point sound is a helper for addAudio where radius is use to define the size of the audio source. The audio will get stronger the closer you get to the source. Good for example birds singing in a tree.

radius in meters, approximately.

const cube = getPrefab({ p: [0, 0, 5] });
addPointSound(cube, 'a_am_birds_and_insects_01', 10);
addTo(root, cube);

Omnipresent Sound

Add a omnipresent sound source using addOmnipresentSound. This adds a point of sound with the same volume in the entire world.

addOmnipresentSound(root, 'a_am_rainforest_01');

Sound Cell

Add a sound cell using addSoundCell. The sound will play with the same volume within the radius. A sound cell is useful in situations where you want sound to be played within a certain area, such as a room, but not outside of it.

radius in meters, approximately.

const soundCenter = getGroup({ p: [0, 1, 0] });
addSoundCell(soundCenter, 'a_mu_bass_meant_jazz_kevin_macleod_01', 10);
addTo(root, soundCenter);

Dummies

You can place dummy avatars in a scene using getDummy.

addTo(root, getDummy());

getDummy

You change avatar and it's emot like this:

addTo(root, getDummy({ avatar: 'red_robot', emot: 'an_default_emote_shuffledance' }));

You can also change properties like scale, speed and material.

addTo(
  root,
  getDummy({
    avatar: 'red_robot',
    emot: 'an_default_emote_shuffledance',
    speed: 0.2,
    s: pop3(3),
    material: 't_lava_01',
  })
);

lava robot

Or why not add a video?

let dancer = getDummy({
  avatar: 'red_robot',
  emot: 'an_default_emote_shuffledance',
  speed: 0.5,
  s: pop3(7),
  p: [0, -6, 0],
});
addVideo(dancer, { src: 'https://cdn.hibervr.com/video/Tunnel_Loop.mp4', emissiveStrength: 2 });
addTo(root, dancer);

video robot

Ready Player Me

You can also add customized avatars from Ready Player Me. For the URL to work it must be an HiberWorld avatar created on https://readyplayer.me/hub/avatars.

The URL is found in the three-dots-menu -> Copy .glb URL.

Add a Ready player me dummy using getExternalAvatar.

const rpmAvatar = getExternalAvatar({
  url: 'https://api.readyplayer.me/v1/avatars/6345138aabc8b2f2ee9c21b1.glb',
  skeleton: 'SKG_RPM_MALE',
  p: [0, 0, 5],
  r: [0, 180, 0],
  emot: 'an_default_backflip',
});
addTo(root, rpmAvatar);

RPM

Particles

HiberEngine has a highly capable particle system. To add particles to an object use addParticles

const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 3], s: pop3(0.3) });
addParticles(ball);
addTo(root, ball);

basic particles

You can use particles om animated objects. Important to remember to add the particle to the object to be animated and not to the Animated Node returned from getMoving.

const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 2], s: pop3(0.3) });
addParticles(ball); // <-- add particles here
addTo(root, getMoving(ball));

Add properties for the particle system like this:

const ball = getPrefab({ id: 'sphere_01', p: [0, 0, 2], s: pop3(0.3) });
addParticles(ball, {
  particleTexture: 'particle_star_01_albedo',
  sizeStart: 0.25,
  sizeEnd: 0.1,
  forceGravity: -10,
  randomVelocity: [2, 5, 2],
  gradientTexture: 'particle_gradient1_albedo',
  colorEnd: [1.0, 1.0, 1.0, 1.0],
  collisionMode: 'BOUNCE',
});
addTo(root, getMoving(ball));

sparks

All settings found here addParticles.ts.

You can also add particles using the particle presets found here PARTICLE_PRESETS.ts.

const cube = getPrefab({ p: [0, 0, 5] });
addParticles(cube, PARTICLE_PRESETS.starDust);
addTo(root, getMoving(cube));

Get a room

Create a room by using getRoom. Returns a room with walls defined in the wallTypes property. Walls are created using getSlice3 and getWall

const room = getRoom({
  p: [0, 0, 5],
  wallTypes: [WALL_TYPES.DOOR, WALL_TYPES.WINDOW, WALL_TYPES.DOOR, WALL_TYPES.PLAIN],
});
addTo(root, room);

room

You can also create a house complex using getComplex.

const floor = getComplex({
  p: [0, 0.1, 10],
  dim: 5,
  height: 5,
  rooms: [
    { coords: [0, 0, 0], wallTypes: ['PLAIN', 'PLAIN', 'DOOR', 'NONE'] }, // Define wall types
    { coords: [1, 0, 0], roomType: 'CONNECTED' }, // Or randomize them using roomType
    { coords: [1, 0, 1], roomType: 'OPEN' },
    { coords: [1, 0, 2], roomType: 'CONNECTED' },
    { coords: [2, 0, 2], roomType: 'WINDOWS' },
    { coords: [2, 0, 0], roomType: 'CONNECTED' },
  ],
});
addTo(root, floor);

Random vales

Since we want a scene to look the same every time we load it, we can't use the regular Math.random() function. Instead we use a random seed function.

A random seed function returns a sequence of numbers that appears to be random, but will be the same every time it is run, based on a value called a seed that is provided to the function.

You get random seed values using getRandomSeed.

getRandomSeed().value();

Or use if the shorthand alias:

rsd().value();

This will return a value between 0 and 1. Since in this case no seed is sent to getRandomSeed(seen) the globalSeed value will be used found here globalSeed.

If you are not satisfied with the sequence of random numbers generated by the function, you can specify your own seed number by providing any integer value greater than or equal to 1.

getRandomSeed(42).value();

This feature can be useful if, for example, you have a helper that is placing trees randomly on the scene and another helper that is creating an art gallery with a random layout. Both helpers are using the global random seed by default. If you are not satisfied with the way the trees are being placed, one way to change this is to use a different seed value. You can also change the global seed value but it will effect all values using it.

You can get a random between two values using:

getRandomSeed().range(0, 100); // float
Math.round(getRandomSeed().range(0, 100)); // int

And random item from an array.

getRandomSeed().fromArray(myArray);

Scape

Scape is a helper that is used to distribute objects in a specific area, such as a landscape, cityscape etc. It is an efficient way to add a large number of objects to a scene in a seemingly random placement.

With a few lines of code using getScape you can get create something like this.

addTo(root, getScape(SCAPE_PRESETS.foliagesBig));
addTo(root, getScape(SCAPE_PRESETS.rocks));
addTo(root, getScape(SCAPE_PRESETS.palmTree));

scape-running

The properties for scape is this:

areaSize The size of the scape area. Default is 200.

spaceSize and spaceSizeDiff The smallest and biggest space to place and item inside. "spaceSize" is between spaceSize and spaceSize + spaceSizeDiff. Default 10 and 20.

gapSize and gapSizeDiff Along with this spaces you can also insert gaps with no items inside them.

gapFreq How frequent should the gaps be? For example if you set gapSize and spaceMax to the same number and gapFreq to zero you will ge ta grid.

boundaryMin If you want a glade in the middle of your scape area you define it with boundaryMin. The number is between 0 - 1.5.

boundaryMax Similar to boundaryMin but defined the outer limit of the forest sp to speak.

itemScale The start scale of the items placed in the space.

itemScaleDiff Items placed in spaces will have a scale between itemScale and itemScale + itemScaleDiff.

prefabId Which prefabs to use. Can be a string or an array of prefabsIds.

material Which material to use. Can be a string or an array of materialIds.

tiltiness How mush should the items tilt. Good for creating a wild forest. Value between 0 and 1.

randomRotation Will rotate the item randomly between 0 and 360 on the Y axis.

Over this you can also p, r, s and seed.

Lets have a closer look. Calling getScape with it's default values and you will get this:

addTo(root, getScape());

scape vanilla

As mentioned above in the section about random seed, you can change the layout of the rocks by adding a custom seed value.

addTo(root, getScape({ seed: 42 }));

If we want to make the number of rocks denser we can change spaceSize and spaceSizeDiff values.

addTo(root, getScape({ spaceSize: 5, spaceSizeDiff: 5 }));

scape alter

If we would like to get tilted candy assets in different sizes we could type this:

addTo(
  root,
  getScape({
    spaceSize: 5,
    spaceSizeDiff: 5,
    prefabId: ['lollipop_03', 'lollipop_01', 'lollipop_02', 'marshmallow_pillar_01'],
    tiltiness: 0.1,
    itemScale: 2,
    itemScaleDiff: 5,
    boundaryMin: 0.05,
  })
);

candy forest

As seen above you can also use a preset. To alter a preset you can use this:

addTo(root, getScape({ ...SCAPE_PRESETS.foliages, ...{ r: [180, 0, 0], p: [0, 10, 0] } }));

onEach

Scape also has a property called onEach which allows you to specify a function to be called for each space that is created, rather than automatically inserting a prefab. This allows you to have more control over what happens in each space.

Using that we can do something like this:

const lightCluster = getScape({
  p: [0, 4, 0],
  spaceSize: 10,
  spaceSizeDiff: 20,
  itemScaleDiff: 3,
  onEach: (props: any) => {
    const cyl = addSpotlight(getPrefab({ ...props, ...{ id: rsd().fromArray(['tron_e3_orange', 'tron_e3_green']) } }), {
      color: getRandomColor(),
    });
    return cyl; // <-- Important to return what's been added.
  },
});
addTo(root, getSpinning(lightCluster, { p: [0, 0, 10], axis: 'y', r: [6, 0, 0], duration: 16 }));

scape neon

Note: It's important to return what's been added from the onEach function

Documentation

Check the docs for mor in-depth information.

3.4.0

1 year ago

3.6.1

1 year ago

3.6.0

1 year ago

3.5.0

1 year ago

3.3.1

1 year ago

3.3.0

1 year ago

3.2.9

1 year ago

3.2.8

1 year ago

3.2.7

1 year ago

3.2.6

1 year ago

3.2.5

1 year ago

3.2.4

1 year ago

3.2.3

1 year ago

3.2.2

1 year ago

3.2.1

1 year ago

3.2.0

1 year ago

3.1.9

1 year ago

3.1.8

1 year ago

3.1.7

1 year ago

3.1.6

1 year ago

3.1.5

1 year ago

3.1.4

1 year ago

3.1.3

1 year ago

3.1.2

1 year ago

3.1.1

1 year ago

3.1.0

1 year ago

3.0.12

1 year ago

3.0.11

1 year ago

3.0.10

1 year ago

3.0.9

1 year ago

3.0.8

1 year ago