JavaScript preprocessors can help make authoring JavaScript easier and more convenient. For instance, CoffeeScript can help prevent easy-to-make mistakes and offer a cleaner syntax and Babel can bring ECMAScript 6 features to browsers that only support ECMAScript 5.

Any URL's added here will be added as `<script>`

s in order, and run *before* the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

` ````
// changing these parameters can strongly affect emergent behaviour
var centering_factor = 0.002;
var avoidance_factor = 2;
var agent_optimal_distance = 0.01;
// our agents:
var agents = [];
// some obstacles:
var obstacles = [];
function make_predator() {
var a = {
pos: new vec2(random(), random()),
vel: vec2.random(random() * 0.004),
acceleration: new vec2(),
size: 1/30,
flipper_rate: 5*(random() + 1),
flipper_amount: 0.05,
max_speed: 0.008,
max_force: 0.0005,
field_of_view: 2,
range_of_view: 20,
type: "predator",
};
agents.push(a);
return a;
}
function make_agent() {
var a = {
pos: new vec2(random(), random()),
vel: vec2.random(random() * 0.004),
acceleration: new vec2(),
size: (random() + 1)/160,
flipper_rate: 30*(random() + 1),
flipper_amount: 0.5,
max_speed: 0.004,
max_force: 0.0002,
field_of_view: 2.5,
range_of_view: 5,
type: "prey",
};
agents.push(a);
return a;
}
// make a few agents:
make_predator();
for (var i=0; i<50; i++) make_agent();
// make obstacles:
function make_obstacle() {
var p = {
pos: new vec2(random(), random()),
size: (random() + 1)*0.03
};
obstacles.push(p);
return p;
}
for (var i=0; i<10; i++)make_obstacle();
function move_agent(a) {
// forward Euler integration + constraints
a.vel.add(a.acceleration).limit(a.max_speed);
a.pos.add(a.vel).wrap(1);
}
function update_agent(a) {
// we will compute a desired velocity
// intially, a simple random walker
// (take current velocity and rotate it slightly)
var flipper = Math.sin(a.flipper_rate * now);
var desired_velocity = a.vel.clone()
.rotate(a.flipper_amount*flipper)
.setmag(a.max_speed * random());
// useful to know our direction:
var dir = a.vel.angle();
// information to gather about visible neighbors:
var neighbours = 0;
// these are for the three steering forces:
var neighbour_locations = new vec2();
var neighbour_velocities = new vec2();
var neighbour_avoidances = new vec2();
// check for visible neighbours:
// (might not be the most optimal, but it is simple):
for (var n of agents) {
if (n == a) continue; // don't count yourself!
// get the (relative) vector to the neighbor from the agent:
// (clone() so that we don't modify n.pos)
var rel = n.pos.clone().sub(a.pos);
// because we are in toroidal space, spanning borders,
// there can be more than one relative vector
// this call makes sure we get the shortest one:
rel.relativewrap(1); // 1 is the size of our world
// to get the view distance, subtract sizes,
// (want distance between bodies, not between centers)
var distance = Math.max(rel.len() - a.size - n.size, 0);
// is the neighbour close enough to be seen?
var in_visible_range = distance < a.range_of_view * n.size;
// now rotate this into the view of the agent (global-to-local):
// (i.e. directly in front of the agent is an angle of zero)
var viewrel = rel.clone().rotate(-dir);
// is the neighbor within the agent's field of view?
// use absolute value to capture left & right sides:
var in_visible_angle = Math.abs(viewrel.angle()) < a.field_of_view;
// neighbour seen if within range & field of view:
if (in_visible_range && in_visible_angle) {
if (a.type == "prey" && n.type == "predator") {
// flee!
var fleepath = rel.clone().negate().normalize();
neighbour_avoidances.add(fleepath);
} else {
// yes -- add to count of neighbours
neighbours++;
// accumulate relative locations for centering force
neighbour_locations.add(rel);
// rotate neighbour velocity into agent's perspective,
// accumulate for aligning force
var relative_velocity = n.vel.clone().rotate(-dir);
neighbour_velocities.add(n.vel);
// are we likely to collide?
// compute from where we are *going* to be
var npos1 = n.pos.clone().add(n.vel);
var apos1 = a.pos.clone().add(a.vel);
var rel1 = npos1.sub(apos1);
rel1.relativewrap(1);
var distance1 = Math.max(rel1.len() - a.size - n.size, 0);
// feel uncomfortable if the neighbour is too close:
// e.g. closer than optimal distance
var optimal_distance = agent_optimal_distance;
var negative_feeling = Math.max(0, optimal_distance - distance);
if (negative_feeling > 0) {
var normalized = negative_feeling / optimal_distance;
if (a.type == "predator") {
neighbour_avoidances.add(rel.clone().setmag(+Math.sqrt(normalized)));
} else {
neighbour_avoidances.add(rel.clone().setmag(-Math.sqrt(normalized)));
}
}
}
}
}
for (var p of obstacles) {
// get the (relative) vector to the neighbor from the agent:
// (clone() so that we don't modify n.pos)
var rel = p.pos.clone().sub(a.pos);
// because we are in toroidal space, spanning borders,
// there can be more than one relative vector
// this call makes sure we get the shortest one:
rel.relativewrap(1); // 1 is the size of our world
// to get the view distance, subtract sizes,
// (want distance between bodies, not between centers)
var distance = Math.max(rel.len() - a.size - p.size, 0);
// is the neighbour close enough to be seen?
var in_visible_range = distance < a.range_of_view * p.size;
// now rotate this into the view of the agent (global-to-local):
// (i.e. directly in front of the agent is an angle of zero)
var viewrel = rel.clone().rotate(-dir);
// is the neighbor within the agent's field of view?
// use absolute value to capture left & right sides:
var in_visible_angle = Math.abs(viewrel.angle()) < a.field_of_view;
if (in_visible_range && in_visible_angle) {
// feel uncomfortable if the neighbour is too close:
// e.g. closer than optimal distance
var optimal_distance = agent_optimal_distance;
var negative_feeling = Math.max(0, optimal_distance - distance);
if (negative_feeling > 0) {
var normalized = negative_feeling / optimal_distance;
neighbour_avoidances.add(rel.clone().setmag(-Math.sqrt(normalized)));
}
}
}
// did we see anyone?
a.sees_neighbours = neighbours > 0;
if (a.sees_neighbours) {
// apply scaling to forces:
neighbour_locations.mul(centering_factor / neighbours);
desired_velocity.add(neighbour_locations);
if (a.type == "prey") {
neighbour_velocities.div(neighbours);
desired_velocity.add(neighbour_velocities);
}
}
// avoidances can happen even without neighbours (obstacles)
neighbour_avoidances.mul(avoidance_factor);
desired_velocity.add(neighbour_avoidances);
// to convert desired_velocity into a steering force,
// need to subtract current velocity
a.acceleration = desired_velocity.sub(a.vel);
// apply constraints:
a.acceleration.limit(a.max_force);
}
function update() {
// to separate passes to prevent artefacts
// (similar to double-buffering)
for (var a of agents) {
update_agent(a);
}
for (var a of agents) {
move_agent(a);
}
}
function draw() {
draw2D.color("grey");
for (var p of obstacles) {
draw2D.circle(p.pos, p.size);
}
for (var a of agents) {
// push into agent's local coordinate system
draw2D.push().translate(a.pos).rotate(a.vel).scale(a.size);
// draw agent body:
var hue = a.type == "predator" ? 0.7 : 0.5;
var active = a.sees_neighbours ? 0.7 : 0.4;
draw2D.hsl(hue, active, active);
draw2D.rect();
// draw agent eyes:
draw2D.color("white");
draw2D.circle([0.5, 0.5], 0.5);
draw2D.circle([0.5,-0.5], 0.5);
// done drawing agent:
draw2D.pop();
}
}
// click to add more agents:
function mouse(e, pt) {
if (e == "down") {
var a = agents[0];
a.pos.set(pt);
}
}
```

999px

Loading
..................

Alt F
Opt F
Find & Replace

Also see: Tab Triggers