@wonderyard/vivarium 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -7
- package/dist/blueprint/rule-blueprint.d.ts +2 -1
- package/package.json +9 -1
- package/README.md.local +0 -93
package/README.md
CHANGED
|
@@ -1,28 +1,93 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Vivarium
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
⚠️ Work in progress ⚠️
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
Vivarium is a TypeScript library that aims to provide a simple yet expressive way of creating your own cellular automata and run simulations on the web.
|
|
8
|
+
|
|
9
|
+
Vivarium exposes a minimal API that attempts to express complex rules and conditions in a way that resembles natural language. Concepts and technical terms from the cellular automata theory are almost transparent to the user. This way the cognitive effort of translating ideas into code is reduced. By lowering this barrier we enable fast prototyping during the creative coding process, especially for users who are approaching the cellular automata world for the first time.
|
|
10
|
+
|
|
11
|
+
Read the **WIP** [docs] to get started, or continue reading for a quick overview and **WIP** [demo].
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
4
14
|
|
|
5
15
|
### Install
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
Install Vivarium as a dependency. No other dependecies required.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i @wonderyard/vivarium
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
8
24
|
pnpm add @wonderyard/vivarium
|
|
9
25
|
```
|
|
10
26
|
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @wonderyard/vivarium
|
|
29
|
+
```
|
|
30
|
+
|
|
11
31
|
### Import
|
|
12
32
|
|
|
33
|
+
Import it once and use it anywhere.
|
|
34
|
+
|
|
13
35
|
```TypeScript
|
|
14
|
-
import { vivarium } from "@wonderyard/vivarium";
|
|
36
|
+
import { vivarium, setup } from "@wonderyard/vivarium";
|
|
15
37
|
```
|
|
16
38
|
|
|
17
|
-
###
|
|
39
|
+
### Create
|
|
40
|
+
|
|
41
|
+
To give you an idea of what the API can offer and how intuitive it can be to reason about rules and conditions, we recreated the most popular cellular automaton: [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). Now featuring (with a little bit of imagination) orange cats 🐈
|
|
42
|
+
|
|
43
|
+
> We suggest you to read about Game of Life and how it works before continuing reading. If you don't understand clearly what the following code is representing, it's okay! Visit the **WIP** [docs] for a gentler introduction to cellular automata and what vivarium is meant for.
|
|
18
44
|
|
|
19
45
|
```TypeScript
|
|
20
46
|
const vi = vivarium();
|
|
21
47
|
|
|
22
|
-
const space = vi.element("space", "
|
|
48
|
+
const space = vi.element("space", "white");
|
|
23
49
|
const cat = vi.element("cat", "orange");
|
|
24
50
|
|
|
51
|
+
// A cat is born if there's a family of 3 in the area.
|
|
25
52
|
space.to(cat).count(cat, 3);
|
|
26
|
-
|
|
53
|
+
|
|
54
|
+
// The cat stays if the area is neither too empty nor too crowded...
|
|
55
|
+
cat.to(cat).count(cat, 2, 3);
|
|
56
|
+
|
|
57
|
+
// ...otherwise the cat will leave the area forever.
|
|
27
58
|
cat.to(space);
|
|
59
|
+
|
|
60
|
+
const life = vi.create();
|
|
28
61
|
```
|
|
62
|
+
|
|
63
|
+
### Run
|
|
64
|
+
|
|
65
|
+
```TypeScript
|
|
66
|
+
// Create a new canvas (or you could use an existing one)
|
|
67
|
+
const canvas = document.createElement(canvas);
|
|
68
|
+
document.body.appendChild(canvas);
|
|
69
|
+
|
|
70
|
+
// Set the canvas size. This will be the automaton size as well.
|
|
71
|
+
const size = 512;
|
|
72
|
+
canvas.width = size;
|
|
73
|
+
canvas.height = size;
|
|
74
|
+
|
|
75
|
+
// Pass the canvas and the automaton you created to the setup function:
|
|
76
|
+
const { evolve } = setup({ canvas, automaton: life });
|
|
77
|
+
|
|
78
|
+
// Create a simple loop that evolves the canvas:
|
|
79
|
+
const loop = async () => {
|
|
80
|
+
await evolve();
|
|
81
|
+
requestAnimationFrame(loop);
|
|
82
|
+
}
|
|
83
|
+
requestAnimationFrame(loop);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**WIP** [See the live demo here]
|
|
87
|
+
|
|
88
|
+
## Browser compatibility
|
|
89
|
+
|
|
90
|
+
Vivarium runs its simulation entirely on the GPU thanks to the WebGPU API and its compute shaders. WebGPU is a relatively new technology. It is available since Chrome 113, Safari 26. See implementation status in other browsers [here](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status). Vivarium has been tested on Chrome 139 for macOS and Safari 26.0 for macOS, showing better performances on Chrome overall.
|
|
91
|
+
|
|
92
|
+
## Contributing
|
|
93
|
+
Although I've been researching and experimenting with cellular automata for years, this is my first WebGPU-based project, so it might be rough around the edges. With WebGPU I've been able to achieve something it wouldn't be possible in a browser otherwise: to build something simple, customizable, and accessible for the end user, without compromising on performance. The intent is to offer this library as a learning tool and something other people can build on top of. If you share the vision and you would like to improve this software, PRs are welcome.
|
|
@@ -8,7 +8,8 @@ export declare class RuleBlueprint extends BaseBlueprint {
|
|
|
8
8
|
protected rule: Rule;
|
|
9
9
|
constructor(context: BlueprintContext, automaton: Automaton, rule: Rule);
|
|
10
10
|
private condition;
|
|
11
|
-
count(refBlueprintOrNeighbor: RefBlueprint | AnyNeighbor, count
|
|
11
|
+
count(refBlueprintOrNeighbor: RefBlueprint | AnyNeighbor, count: number[]): RuleBlueprintWithAccept;
|
|
12
|
+
count(refBlueprintOrNeighbor: RefBlueprint | AnyNeighbor, ...count: number[]): RuleBlueprintWithAccept;
|
|
12
13
|
is(neighbor: AnyNeighbor, refBlueprintOrNeighbor: RefBlueprint | AnyNeighbor): RuleBlueprintWithAccept;
|
|
13
14
|
chance(part: number, whole?: number): RuleBlueprintWithAccept;
|
|
14
15
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderyard/vivarium",
|
|
3
3
|
"description": "Modern, intuitive, WebGPU-powered toolkit for creating your own cellular automata.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"repository": "https://github.com/WonderYard/vivarium",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "OrangeNote",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"cellular-automata",
|
|
10
|
+
"compute",
|
|
11
|
+
"creative-coding",
|
|
12
|
+
"game-of-life",
|
|
13
|
+
"simulation",
|
|
14
|
+
"webgpu"
|
|
15
|
+
],
|
|
8
16
|
"type": "module",
|
|
9
17
|
"files": [
|
|
10
18
|
"dist"
|
package/README.md.local
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# vivarium
|
|
2
|
-
|
|
3
|
-
A _vivarium_ is a set of elements that interact with each other in a grid, changing into other elements over time. With this library you can define your vivarium and see it evolve on your screen.
|
|
4
|
-
|
|
5
|
-
You can start defining your vivarium like this:
|
|
6
|
-
|
|
7
|
-
```TypeScript
|
|
8
|
-
import { vivarium } from "@wonderyard/vivarium";
|
|
9
|
-
|
|
10
|
-
const vi = vivarium();
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Elements
|
|
14
|
-
|
|
15
|
-
An _element_ is the basic bit of your simulation. It can be represented with a little square on your screen.
|
|
16
|
-
|
|
17
|
-
Here's how to define one.
|
|
18
|
-
|
|
19
|
-
```TypeScript
|
|
20
|
-
const cat = vi.element("cat", "orange");
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
We're not drawing it on the screen yet. For now we are just defining what a cat is, so that our system is aware of its existence in the vivarium.
|
|
24
|
-
|
|
25
|
-
## Kinds
|
|
26
|
-
|
|
27
|
-
The second base concept of vivarium is _kinds_. A kind is not an element, it doesn't have a color and so it cannot be directly represented on a screen. However it can be inherited by elements to share common characteristics. It is not necessary to use _kinds_. Consider it an advanced concept that allows you to write less code and achieve more complex. To start, I'll show you how to define one, which is similar to how we define elements.
|
|
28
|
-
|
|
29
|
-
```TypeScript
|
|
30
|
-
const animal = vi.kind("animal");
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Rules
|
|
34
|
-
|
|
35
|
-
The heart of vivarium is made of _rules_. A rule is a way to tell elements how to behave in our vivarium. Every step of the simulation, an element can become another element or stay the same. Let's see a simple rule first:
|
|
36
|
-
|
|
37
|
-
```TypeScript
|
|
38
|
-
const space = vi.element("space", "white");
|
|
39
|
-
const cat = vi.element("cat", "orange");
|
|
40
|
-
|
|
41
|
-
space.to(cat);
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
The method `to` can be read as "becomes" or "evolves to". So with this last line, what we are saying is: when we will run the system, at every simulation step, every space element will become a cat element. It's like cats appearing out of nowhere, so on the screen we would see every white cell in the grid becoming orange as soon as we start the simulation. A room filled with cats!
|
|
45
|
-
|
|
46
|
-
## Conditions
|
|
47
|
-
|
|
48
|
-
However in a system like that, nothing interesting would happen after the first step. All free spaces are occupied by cats immediately and they will just sit there forever.
|
|
49
|
-
|
|
50
|
-
Let's create a more interesting system, this time by adding a condition to the existing rule.
|
|
51
|
-
|
|
52
|
-
```TypeScript
|
|
53
|
-
space.to(cat).count(cat, 1);
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
By calling the method `count` we are stating that every time the simulation attempts to apply the rule `space.to(cat)` it must first count the number of whatever we pass as first parameter (`cat` in this case) in from the neighborhood of `space` and compare that with the second argument (`1` in this case). If the condition is satisfied, that is, the number of cats in the neighborhood of space is exactly 1, then the rule is applied, otherwise it is skipped for the current step.
|
|
57
|
-
|
|
58
|
-
With this rule, we are filling the room bit by bit in an interesting pattern. It almost looks like a net made of cat tails! Eventually the simulation becomes stable and nothing else happens.
|
|
59
|
-
|
|
60
|
-
## Game of Life
|
|
61
|
-
|
|
62
|
-
Probably the most famous cellular automata, Game of Life rules are not that far from what we created just before. There's many ways to implement Game of Life rules. Here's one version:
|
|
63
|
-
|
|
64
|
-
```TypeScript
|
|
65
|
-
// A new cat is born near a family of 3
|
|
66
|
-
space.to(cat).count(cat, 3);
|
|
67
|
-
// Underpopulation: the cat leaves the space because it feels lonely there
|
|
68
|
-
cat.to(space).count(cat, [0, 1]);
|
|
69
|
-
// Overpopulation: too many cats!
|
|
70
|
-
cat.to(space).count(cat, [4, 5, 6, 7, 8]);
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Feel free to come up with your own implementation! Vivarium is designed to allow you to define the same thing in multiple ways, depending on the structure and conventions you want to use.
|
|
74
|
-
|
|
75
|
-
Here's an equivalent list of rule declarations:
|
|
76
|
-
|
|
77
|
-
```TypeScript
|
|
78
|
-
// Same as before
|
|
79
|
-
space.to(cat).count(cat, 3);
|
|
80
|
-
// Cat stays there when the number of cats around it is just right
|
|
81
|
-
cat.to(cat).count(cat, [2, 3]);
|
|
82
|
-
// Cat vanishes :'(
|
|
83
|
-
cat.to(space);
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Here we compressed the two `cat.to(space)` rules into one inverse rule, and we let it evolve to space otherwise, unconditionally. Try to reason on why this set of rules is equivalent to the previous one and notice two important things:
|
|
87
|
-
|
|
88
|
-
- Rules without conditions are still meaningful, so use them when they might simplify your overall rule syntax.
|
|
89
|
-
- Order of rules matters!
|
|
90
|
-
|
|
91
|
-
## Order of rules
|
|
92
|
-
|
|
93
|
-
Did I mention it already? Order of rules matters! Yes, because the system has to evaluate rules one by one. Whichever rule satisfies its conditions first, will be the one evolving the cell. No more rules are evaluated for that cell until the next simulation step. In other words, rules are declared in order of priority, high to low. The first rule passing the test is the winner. Every other rule is ignored. So it's important that you design your vivarium with this concept in mind, or your rules won't work as expected.
|