SkillAgentSearch skills...

G9

Automatically Interactive Graphics 🖼✨💯

Install / Use

/learn @bijection/G9
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

g9.js

g9 is a javascript library for creating automatically interactive graphics.

With g9, interactive visualization is as easy as visualization that isn't. Just write a function which draws shapes based on data, and g9 will automatically figure out how to manipulate that data when you drag the shapes around.

For example, the following code:

g9({
    x: 0,
    y: 0
}, function(data, ctx){
    ctx.point(data.x, data.y)
    ctx.point(data.y, data.x)
})
.insertInto('#container')

draws two dots at (x,y) and (y,x). With no additional effort, it's interactive - dragging either of the dots changes the value of x and y, and moves the other dot:

basic demo

This automatic interactivity makes it trivial to manipulable complex visualizations like this fractal:

dragon demo

You can see (and play with!) more examples on the website, or head to the docs for a full treatment of the API.

Installation

You can use g9 with npm (if you're using webpack or browserify) or with a script tag:

npm

npm install g9
var g9 = require('g9')
// or 
import g9 from 'g9'

<script/>

First download a copy of g9 here, then

<script src='path/to/g9.js'></script>
 
<script>
    // g9 is now defined 
</script>

You can also hotlink a copy of g9 from a CDN:

<script src='https://cdnjs.cloudflare.com/ajax/libs/g9/1.0.16/g9.js'></script>
 
<script>
	// g9 is now defined 
</script>

Docs

g9(initialData, render[, onRender]): g9Canvas

This is the main g9 function, which returns a g9Canvas which you can mount in your page with the g9Canvas.insertInto(selectorOrDOMNode) method. For example:

g9({foo: 10}, function(data, ctx){
	ctx.point(data.foo, 17)
})
.insertInto('#container')

The g9Canvas API is covered here.

g9() takes the following arguments:

initialData

initialData is a flat object with numeric values, which will be used in the first call to render. For example:

var initialData = {
    foo: 10
}

render(data, ctx: g9Context)

render(data, ctx) is a function that receives a data object with the same keys as initialData, but possibly different values, and a g9Context ctx. The g9Context API is covered here.

render is responsible for calling methods on ctx to produce a drawing. For example:

function render(data, ctx){
    ctx.point(data.foo, 17)
}

creates a point at x-coordinate data.foo and y-coordinate 17.

When someone interacts with the graphics, for example by trying to drag an element to a new position, g9 optimizes over the space of possible values for data to find a set of values that comes closest to creating the change. In the preceeding example, if data.foo is initially 10 and you tried to drag the point to the left, g9 might come up with

{
    foo: 8
}

After optimization, g9 rerenders the entire scene with the new data, so that everything is consistent.

onRender(data, renderedObjects)

onRender(data, renderedObjects) is an optional argument which, if included, is called after each re-render with the data that determined the render, and the set of rendered objects. Typical uses for onRender include debugging compositions and updating other parts of your page.

g9Canvas

A g9Canvas is the object returned by a call to g9(initialData, render, onRender).

g9Canvas.insertInto(selectorOrDOMNode)

Mounts the graphics' svg node as a child of selectorOrDOMNode, which can be either a selector or a DOM node, and returns the graphics object to enable chaining.

For example:

g9({foo: 10}, function(data, ctx){
	ctx.point(data.foo, 17)
})
.insertInto('#container')

Note: You can change the size of the mounted svg node with javascript, e.g g9Canvas.node.style.height = '500px'; g9Canvas.resize(), or css, e.g. #g9NodesParentElement > svg {width: 100%; height: 300px}.

g9Canvas.align(xAlign, yAlign)

Sets the position of the origin in relation to which graphics are drawn. xAlign and yAlign are both strings that default to 'center'. Returns the graphics object to enable chaining.

  • xAlign can be either 'left', 'center', or 'right'.
  • yAlign can be either 'top', 'center', or 'bottom'.

For example:

g9({foo: 10}, function(data, ctx){
	ctx.point(data.foo, 17)
})
.align('bottom', 'left')
.insertInto('#container')

g9Canvas.node

A read-only reference to the svg DOM node that holds the g9 graphics.

g9Canvas.setData(data)

Sets the data currently being visualized by a g9 instance to data. This is useful for animations. For example:

var graphics = g9({foo: 10}, function(data, ctx){
	ctx.point(data.foo, 17)
})
.insertInto('#container')

var theta = 0

setInterval(function(){
	graphics.setData( {foo: Math.cos( theta += 0.01 )} )
}, 30)

g9Canvas.getData()

Get the data currently being visualized by a g9Canvas. For example:

var graphics = g9({foo: 10}, function(data, ctx){
    ctx.point(data.foo, 17)
})
.insertInto('#container')

alert(JSON.stringify(graphics.getData()))

g9Canvas.isManipulating

A boolean property that's true when a user is manupulating a shape on the g9 canvas.

g9Canvas.resize()

Invalidates the g9 display. Usually a noop, but should be called after programmatically resizing the g9 DOM node or its container.

g9Canvas.desire(id, ...desires)

Internal method, generally safe to ignore, but useful for complex animation. For now, the best way to use this is to read the source.

One usecase for this is to minimize with respect to a different cost function. For example, if the control points are derived from other variables through some hard-to-invert formula, rather than being independent variables, then wiggling a control point will wiggle all of the other control points. This can be unpleasant, as you want the control points to wiggle independently. To solve this, you can set the control point's desired value directly in the data, then use the calculation for the control point to render an invisible node, use your browser's Inpsector to see the id of that invisible node, then specify an onRender function that calls desire(), forcing that invisible node to match the desired value. So the optimizer will wiggle the other variables, and leave the control points fixed!

Sample usage: g9Canvas.desire("auto00", ['i0x', 'i0y'], (b)=>{return (b.cx-g9Canvas.getData().radius0) ** 2;})

  • auto00 is the name of the invisible node in the svg. Rendering it first helps to get a consistent ID.
  • i0x is the id of a variable that should wiggle during minimization.
  • The third function is a cost function, forcing the x coordinate of the invisible node to radius0. It doesn't always succeed (the optimizer isn't so robust). Performance may not be amazing. As far as I know, quadratic error is a good cost function here.

Using an invisible line instead of an invisible point will let you set 4 numbers instead of 2.

g9Context

A new, immutable g9Context object ctx is passed as the second argument to render each time render is called. It has a variety of drawing methods and some read-only properties that the render function uses to create a drawing.

g9Context.[shape]

When calling a drawing method, you can include any number of ordered arguments, optionally followed by an object that specifies further arguments by name, and / or includes svg properties. For example, all of the following are equivalent:

ctx.point(30, 50, ['a'])

ctx.point(30, 50, {affects: ['a']})

ctx.point(30, {
	y: 50,
	affects: ['a']
})

ctx.point({
	x: 30,
	y: 50,
	affects: ['a']
})

The Affects Argument

All g9 shapes take an optional argument affects, a list of key names. When you drag a shape with an affects list, g9 will only change the values of the keys in the affects list.

For example, dragging the following point will only change the value of data.x, and thus the point will only move horizontally

View on GitHub
GitHub Stars2.0k
CategoryDevelopment
Updated9h ago
Forks48

Languages

JavaScript

Security Score

95/100

Audited on Mar 29, 2026

No findings