PrototypeJungle images/logo_alt.html
Kits Tutorial Draw Code Editor Text Editor Coding Guide About
Coding Guide

PrototypeJungle users can (1) build compound items such as diagrams with code, but can also (2) code their own visual elements and kits, and place them in their catalog directories where they become available in PrototypeJungle's UI, and can be shared with other users. In addition, the core technology is available under the MIT Licence for use in any application (3). This document provides the information needed for all three activities.

Sections 4 - 10 describe how to work with prototype trees, the material from which all elements (referred to as "items") are made. These are the sections that are relevant to use of this core technology outside of the PrototypeJungle context. Also, the first three modules of the API section document core technology.

1. Sample code

Before wading into the details, you might like to look at some code. Samples 2,3,4 illustrate how new visual elements are built.

  1. A simple diagram.
  2. An element: rectangle
  3. An element with a handle: roundedRectangle. See the section below about custom controllers.
  4. Building an item from data
  5. A kit: ring

Select the item of interest in the catalog, and then click the "Open Code" button.

2. Working with code

2.1 The Code Editor

The editor enables you to edit code in one window, re-run it at will, and see the results in the graphics panel immediately. If you click on the name of the source file, or on one of the dependencies, an ordinary text editor is popped on the file in question.

If you have signed in, the save as button will be active. Each signed in user is allocated a directory where files are named according to the scheme:


This is where the code editor saves files. You can open the code editor on a file as illustrated by this example:

The main drawing program (also called the "structure editor") can be invoked in a similar way:

In addition, there is a base directory of files with names of the form:


For example:

The base directory is at the PrototypeJungle github repo (MIT license)

The underlying implementation of all of this is in the cloud at Google's FireBase.

2.2 Debuggers

Browser debuggers (eg Chrome's DevTools) work well with PrototypeJungle. However, the usual variants at and catch errors, which is not what is wanted in the context of debugging. The variants and (accepting exactly the same GET arguments) are provided in which errors are not caught.

3. Catalogs and Sharing

The catalog panel is the area on the right side of the screen from which you drag items for insertion/swap. Initially the contents of this panel are taken entirely from a shared source, the global catalog.

However, you can build your own catalog, simply by saving any code-built item in the folder /catalog/, or in a subfolder /catalog/<whatever>. Then, the next time you start up PrototypeJungle, you will see the item under the tab (.c) or (.c)/<whatever> (resp.), where it can be used in insertions and swaps.

If you'd like to share your work, place it in the exports directory. You yourself will have access to its contents under tabs (.x) or (.x)/<whatever>. But so will other people, if they know your user name, controlled by their config file /config.json (accessible at top level from the file pulldown). Initial contents of config.json:


This can be edited to, for example


After this edit, the tab (someUserName) will appear in the catalog panel. When this tab is selected, the contents of that user's exports directory will appear, ready to be dragged in. If the exports directory in question contains subdirectories, tabs of the form (someUserName)/someDirectory will appear. If only the subdirectory is wanted, and not the rest of someUser's exports, then config file should look like this:


More than one exports subdirectory, and more than one user, can be indicated in this manner. Of course, you can edit your own config file to access other people's exports. Generally, only the content indicated in the config file will appear in your catalog panel.

3.1 Viewing Code from the Catalog

If you select an item in the catalog without dragging it into the main panel, it will be outlined with a red box indicating which catalog item has been selected. Then, if you click on "View Source" in the top bar, the code editor will open on the code which implements that catalog item. Within the code editor you can in turn navigate to the dependencies of this code. Try it with the circle.

4. Require

Components are accessed and defined via core.require. Here is an example:

  function (circlePP,arrowPP) {
    let item ='<g/>');
    return item;

This binds the variables circlePP and arrowPP to the components defined in '/shape/circle.js', and '/shape/arcArrow.js', respectively, and then calls the function (ie the last argument to core.require). The value returned by the function is the newly built component.

5. Trees

Now to the nitty-gritty: the structure of PrototypeJungle's data, and the operations which provide access to it.

All prototype trees (aka "items") are trees: each non-atomic child has a unique parent. (Formally, if parent.prop === child, and prop is an own property of parent, there is no parent2 with parent2 !== parent, and with parent2.prop2 === child for own property prop2 of parent2). The internal nodes in items inherit from prototypes core.ArrayNode for sequential, zero-based arrays, and core.ObjectNode for objects which are not arrays.;

creates a new Object, and;

a new Array.


assigns child as a child of object with name name. If child is an Object or Array, this results in setting special properties: child.__parent === object, and child.__name === 'name'.


also assigns child as a child of object, but automatically assigns its name.

For an Array,


pushes child onto the end of array, and assigns array as the parent of child. That is, array[array.length-1] === child, and if child is an Object or Array, and child.__parent === array. Also, child.__name === array.length-1. Arrays are always sequential and zero-based.


takes an "ordinary" JavaScript tree such as one expressable in JSON, and turns it into the PrototypeJungle kind of tree. For example:


will produce a Object/Array tree with matching structure.

Click here for an example of tree construction and manipulation.


var root =;
var aa =;
var bb =;

root.set("a",aa); // adds aa as a child named "a" of root

// __name and __parent "glue the tree together".
bb.__parent === root;

// let's add some atomic data 
aa.x = 5;  // set is not needed for atomic data or functions
aa.f = function (x) {return x*x;}

// now for an Array
var cc =;


this yields:

        /          \
       a            b
      / \            \
     x   f            c
    /     \            \
   5    function      [ *, 5]

where * is an Object

This could also be built with

var root = core.lift({a:{x:5},b:{c:[{z:45},5]}})
root.a.f = function (x) {return x*x};

There is nothing wrong with having properties of Objects that reference nodes in the tree other than children, as in

a.xx = b; 

in the above example.Then xx is a cross-tree reference.

Restriction on names: names may include only letters, numerals, and the underbar, and may not start with a numeral.

6. Instantiation and Serialization

inode = node.instantiate();

builds an instance of node; a tree which has the same structure as node, but inherits primitive data and functions from node via prototypical inheritance. (Details here)

Serialization is performed by:

s = core.stringify(node);

as described here.

7. No news

Nodes are constructed with and, rather than via the use of a "new". The definition of the mk method for Object is: = function () {
  return Object.create(core.ObjectNode);

Recall that Object.create(X) creates a new object with X as prototype.

Object is introduced simply with:

core.ObjectNode = {};

and Array with:

core.ArrayNode = [];

core.ObjectNode itself serves as the prototype for instances. In the more conventional pattern, core.ObjectNode would be a function, and the prototype property of that function, not Object itself, would be the prototype for the instances generated via the new operator (note that if F = function (){}, new F() is equivalent to Object.create(F.prototype))

The function, function.prototype, new   pattern has been available in JavaScript all along, whereas the more direct Object.create was introduced more recent years (in version 1.8.5 to be exact). PrototypeJungle employs the newer pattern, not so much for its intrinsic simplicity (though that's nice), but because this way of doing things has a major simplifying effect on the entire PrototypeJungle code base.

You will never see a "new" anywhere in the code. The convention is that for prototype X, is the creator/initializer (which may take arguments).

8. SVG<markup>);

creates an object that inherits from the prototype svg.Element, which in turn inherits from core.ObjectNode. Its content as an SVG element is as specified by the markup. At this stage, only some of the tags are supported: clipPath, circle, g, line, linearGradient, path, polygon, polyline, rect, radialGradient, stop, text, and tspan.

Here is an example:

var circle =
'<circle fill="rgb(39, 49, 151)" stroke="black" stroke-width="1" r="5" />'

Each svg.Element may have an associated coordinate system/transform. The methods getTransform, and setTransform access this, and operations such as moveto affect it. See the API section for details. As in SVG, the transforms represent coordinate systems relative to parents.

At any given time, the root of the tree being displayed by PrototypeJungle is held in core.root.

An svg.Element is displayed by construction of an associated element in the SVG DOM. The method Element.setDomAttribute(attr,vl) sets the given attribute of the DOM element asscociated with this to the given value. See the SVG documentation for the available attributes. Use of the setDomAttribute method is only occasionally necessary, because each svg.Element has a standard list of properties (eg stroke and fill) that are automatically transferred from the item to the DOM, by setting attributes in the DOM from the values of the item's properties of the same name.

9. Installing Prototypes

Consider this snippet of code, which can be found at /example/two_circles.js

function (circlePP) {
  let item ='<g/>');
  let circleP = core.installPrototype(circlePP); 
  // set the parameters of the circle prototype
  circleP.r = 12;
  circleP.fill = 'blue';
  let circle1 = item.add(circleP.instantiate()).show();
  let circle2 = item.add(circleP.instantiate()).show();
  return item;

Consider the state of PrototypeJungle (that is, of core.root) invoked as follows


Here is what it looks like:

circlePP is an external component. core.installPrototype instantiates that external component, and adds its instantiation in a standard place thereby giving us an internal version circleP of the external component circlePP. That "standard place" is core.root.prototypes. installPrototype(circlePP) is nearly equivalent to

  let circleP = core.root.prototypes.add(circlePP.instantiate().hide());

but leaves out the step of automatically adding the object core.root.prototypes if it is missing. The useful consequence is that any edits to the properties of circleP, being internal to the state, will be retained when the item is saved.

10. The Update Method

In the introductory example, interactivity is implemented "under the hood" via the PrototypeJungle's graph machinery. Here is a variant which has the same behavior, ut it is implemented more directly with no dependencies on graph connections.

  item.update = function () {
    let p1=this.p1,p2 = this.p2;

This method moves circle1 and circle2 to this.p1 and this.p2, respectively, and then causes the arrows to point at the circles. Updates are triggered automatically at load time, and in any circumstance of change. By "a circumstance of change" is meant one in which the implementation detects a potentially relevant event (eg editing properties in the right panel). Update methods, can, of course, be invoked explicitly. Each update method is responsible for triggering updates of its descendants (the automatic updater traverses the tree looking for update methods, but when such a method is found, it does not descend the tree further).

The code for the variant includes support for dragging in the method dragStep, which will be expla.ined in a moment.

The method

 item.initialize = function () {

may be defined as well. If defined, this method is called once when the item in question is added, whether via code (core.requires), or the user interface.

11. Defining a Visual Element

In order to support resizing, an item that is intended to play the role of a visual element appearing in a catalog should follow this rule: It should define the parameters width and height, and its update method should adjust the item's SVG content to reflect these dimensions. The figure should be centered on the local origin. Examples are




In cases where the width and height are always identical, the parameter dimension should be used instead of width and height, as in


The following lines should be included if the item you are defining is to be draggable and resizable:

 item.resizable = true;
 item.draggable = true;

To make use of a new visual element, its implementation should be stored in your catalog directory. How this is done is explained in an earlier section.

12. Containers and Connectors

The visual elements rectangle, lozenge, and circle mentioned above handle display only. However these shapes are mostly useful as components (the border) of a container element which supports placement of text and images within the container. It is the container that you see in the global catalog, not the underlying shape. It is also desirable to support graph operations on containers. For example, we wish to be able to connect any two containers with an arrow that follows the containers around when dragged. Luckily, a few simple incantations are all that is needed to build a graph-respecting container from its border element. To see how, have a look at


As you'll see, the code is short and simple. The work is done in these lines:

function (borderPP,containerP) {
let item = containerP.instantiate();
// properties to be transferred to the border
// This makes the item suitable for use as a graph vertex.

The code for every container in the global library is similar. The only variation is in which "PeripheryOps" are installed (these operations define where connectors should terminate). There are at present three alternatives: graph.installCirclePeripheryOps, graph.installRectanglePeripheryOps, and graph.installLozengePeripheryOps.

Line-like shapes (such as line itself) can serve as components of connectors in an analogous fashion. As is the case for /shape/rectangle.js, /line/line.js and, for example, /line/wavyLine.js, only support drawing. A connector is something that can attach to containers, and follow them around. Connectors may also have an associated label. The code for the wavy line container is at /connector/wavyLine.js Again, it is short and simple; the work is done by:

function (shaft,connectorP) {
let item = connectorP.instantiate();
//the properties to be transferred to the shaft

Again, the code for every connector in the global library is similar.

13. Building an Item from Data

In the standard catalog, there are several items that are defined from data, such as graphs, trees, and rings. Any user can edit or re-source the data, and rebuild the item to reflect the edit. Working with data is explained in the tutorial.

You can define your own items built from data. How this is done is illustrated by


Here is an extract of the code:

core.require('/container/circle.js',function (circlePP) {

let item ='<g/>');
let dataString = '["a","b","c"]';
item.separation = 50;

item.buildFromData = function (data) {
  ... some code  ...

item.initialize = function () {
  this.circleP = core.installPrototype(circlePP);
  let data = item.initializeData(dataString);

return item;

An item becomes buildable from data whenever you define a method on it called "buildFromData". In this simple example, the data is an array of strings, and buildFromData constructs one circle for each string in the array, and labels the circle with that string. Please have a look at the full code.

Data-built items must also supply initial data, given as a JSON string. The method initializeData (predefined) installs that initial data.

Just as with visual elements, data-built items should be stored in your /catalog directory. That's it!

14. Kits

A kit is an item which incorporates specialized methods for 1) dragging and 2) deletion. A kit may also implement 3) menu buttons for manipulating the kit. Consider the tree kit, for example. It defines specialized dragging (where subtrees travel with their roots), deletion (in which subtrees are deleted along with their roots), and several menu buttons including adding a node as child or sibling, and repositioning a subtree).

1) Dragging.

The relevant code for trees is :

kit.isKit = true;

kit.dragStep = function (vertex,pos) {
 let localPos = this.vertices.toLocalCoords(pos,true);
 /* move all the descendants of vertex to the relative
    positions they had prior to the move of vertex

kit.dragStart = function () {

This code appears in /kit/arrowTree.js

In the construction of a tree from data (buildFromData) we have the line:

  vertexP.draggableInKit = true;

vertexP is the prototype for nodes in the tree.

Whenever a node defined as draggableInKit is dragged, the dragStep method of the diagram is invoked for each increment of dragging the node. Since the prototype vertexP of the vertices is defined as draggable, this property is inherited by the instances. If present, the dragStart method is called with the initial position at the start of the drag.

2) Deletion. kit.__delete(x) works in a similar fashion to allow deletion to be handled in a special way suitable to the kit. If the __delete method is defined for the kit, then when any item x within the kit is deleted, this method is called with x as argument. In the tree case, the subtree depending from x is deleted (as you will see from the code).

3) Menu buttons. The additional menu buttons for the left panel are defined as follows for the tree:

kit.actions = function (node) {
  let rs = [];
  if (!node) return;
  if (node.role === 'vertex') {
     rs.push({title:'Add Child',action:'addChild'});
    if (node.parentVertex) {
      rs.push({title:'Add Sibling',action:'addSibling'});
    rs.push({title:'Reposition Subtree',action:'reposition'});
  if (node === this) {
    rs.push({title:'Reposition Tree',action:'repositionTree'});
  return rs;

As you can see, actions returns a list of objects each with title and method name. When an item in the kit is selected, titles appear in the left panel. Each associated method name should be defined as indicated by the example:

kit.addChild = function (vertex) {
15. Custom Controls

Notice that when you select an arrow, little yellow boxes appear by which you can drag its head and tail around, and resize the head. These little yellow boxes are called "custom controls", or "handles".

To define a custom control, the two methods needed are controlPoints(), and updateControlPoint(index,pos). The controlPoints method should return a core.ArrayNode of geom.Point (s). When the item is selected, yellow handles will appear at the positions returned by controlPoints (the points should be given relative to the item's own origin). Then, when the handles are dragged, updateControlPoint(index,pos) is called at each moment of dragging, with index set to the index of the point which generated the handle being dragged, and pos to its new position. It is the responsibility of updateControlPoint to update and redisplay the item as appropriate given the new handle position. The rounded rectangle provides an example - click on the yellow square to adjust the rounding

If all three methods are defined and adjustable is set, as is the case for the rounded rectangle, the item will be displayed with both a resize box, and the custom handles.

16. Roles, Replacement, and the Transfer State Method

In many items, the visible elements play varying roles. For example in graphs and trees, some shapes play the role of nodes/vertices, and some of edges. A role is assigned in code via:

item.role = <roleName>

When replacing an element in a diagram via "swap" or "swap prototype" in the top bar, only shapes whose role matches the role of the replaced element are presented as possibilities (via a highlight of the candidate shape as the mouse rolls over it).

For any item dest, the method dest.transferState(src, own), if present, transfers state from the replaced shape (src) to its replacement (dest). The own argument indicates whether only own properties should be transfered, or whether the operation should be applied to the relevant inherited properties as well. A common defintion of transferred state for basic shapes such as circles and rectangles is:


//own = consider only the own properties of src
item.transferState = function (src,own) { 
ui.stdProperties is defined in the ui module as :
 const stdTransferredProperties = ['stroke','stroke-width','fill','unselectable',

A kit might define kit.transferElementState(dest,src,own). When a shape S is replaced by R within a kit, this kit.transferElementState(R,S,own) is called as well as R.transferState(S,own). As an example, transferElementState transfers information about the descendant relation in the case of tree kits. The own flag is set or not according to whether a prototype, or an instance is being swapped.

17. Controlling Display of Properties

When an item is selected in the structure editor, its properties and those of its prototype are displayed in the right-hand panel. If a property name begins with a double underbar (eg "__name"), it is not shown, and you can use this convention to hide properties from the user's view. But you can also employ ui.hide as exemplified by the following line from the implementation of the arrow


The subsequent line:


causes the solidHead property to be displayed with a true/false selection box. Similarly, in the implementation of the shadedCircle , the line


causes the outerFill property to be displayed with the Spectrum color picker. By default,this chooser is deployed for all fields named fill or stroke.

By default, the values of properties are editable in the property panel. But with


the given properties are presented in non-editable form.

18. Access to Content

The content that you create on PrototypeJungle is public in the partial sense that anyone who knows the URL of the content has read access to it. This allows you to share content with others by giving them URLs. There is no directory service which would allow discovery by other means. However you should not regard the content as private, either, since guesses (possibly mechanized) are always possible. PrototypeJungle content is somewhat like the content in public repositories on GitHub, but without any directory service. As at GitHub, you are encouraged to express your desires about sharing your content via a license.txt file at top level.

Private content will be supported in the future.

19. API

(Partial listing - more to come)

The core, geom, and svg modules are core technology from the prototypetrees repository.

Calls are given in the form f(arg1:type1,arg2:type2...) where types are: boolean, number,string, Node (core.ArrayNode or core.ObjectNode) or any When a call is described in more detail elsewhere in this document, a link is supplied.

Defaults for property values are given in parentheses just after the name of the property.

Constructor for core.ObjectNode
Constructor for core.ArrayNode
Assign vl as the child of this with name nm
Assign vl as the child of this, with an automatically assigned name.
Remove this from the tree in which it appears.
For each property p in props, set dest[p] = src[p]
core.treeProperties( nd:Node, includeLeaves:boolean)
Returns an array of the direct properties of nd which are edges of the prototype tree (explained above).
An aid to traversing prototype trees. forEachTreeProperty applies the function fn to each of node's tree properties. fn should take inputs of the form (child,property,node), where node is the value passed to forEachTreeProperty, and child=node[property]
Constructor for geom.Point, with properties x and y. If x and y are omitted, 0,0 are used.
Adds point p to this (vector addition)
Scales the point by v
Creates a geom.Point from several kinds of input. If v is an array [a,b] it returns a point with coordinates {x:a,y:b}; if v is a number, a point with coordinates {x:v,y:0}, if v is a geom.Point, a copy of the point.
geom.toPoint() returns the point with coordinates {x:0,y:0}.,
Constructor for geom.Rectangle, with properties corner and extent. For example,20),,5)) has (10,20) as its upper-left corner, and (15,25) as its lower-right corner.
Constructor for geom.Transform, with properties translation, scale, and rotation. Rotation is in radians. All arguments are optional, defaulting to,0), 1, and 0, respectively.
Constructor for svg Elements. s is markup.
If an element has been hidden, change its status to "visible"
Refresh this element. Changes to the PrototypeJungle data for an element (and its descendents) are transferred to the svg model. Adding an element to the PrototypeJungle tree is not reflected
Sets the attribute named attr of the DOM element associated with this. See the SVG documentation for the available attributes.
Returns the transform of this element.
Sets the transform of this element.
Returns the translation of the svg transform of this element.
Move this element to p. That is, set the translation of the svg transform of this element to p.
Return the scale of the transform of this element.
Set the scale of the svg transform of this element to s.
Return the bounds of the given Element (and its descendants). rt is optional. If rt is present, it should be an ancestor of this Element, and the bounds are given relative to rt's coordinate system. If rt is absent, bounds are given in the Element's own coordinate system.
connnects one end of e to v. whichEnd should be 0 or 1.
connnects one end of e to v0, and the other to v1.
moves the ends of the edge as appropriate so that it maintains its connections
updates the whole graph by updating the ends of all edges
Computes where to move the given end to, if that end is being dragged around the periphery
adds vertices and edges instantiated from the prototypes v and ed respectively, as children of n, and as indicated by data. The Cayley D3 graph is built in this way. Here is its data. The format should be self-explanatory to those familiar with JSON.
In the UI, either the prototype or the instance of the selected item can be adjusted (there is a check box for telling which). This global is set to the one being adjusted.
Properties you should set
Node.unselectable:boolean (false)
If this node is clicked, its first selectable ancestor is selected
Node.adjustable:boolean (false)
A resize box appears when this node is selected.
Node.draggable:boolean (false)
Updates and draws all of the nodes that inherit from proto. Frequently used in the form ui.updateInheritors(ui.whatToAdjust), when an edit has been made to the prototype rather instance. See the updateControlPoint method at the bottom of the implementation of the arrow.

These calls control how property values are displayed in the structure editor.

ui.hide(nd:Node,props:array of string)
ui.freeze(nd:Node,props:array of string)
Methods you define
Node.controlPoints():array of Point