A Generator That You'll Love

Usage no npm install needed!

<script type="module">
  import gentyl from '';


npm version Build Status Join the chat at

What is Structural Synthesis and what is it for?

The purpose of this tool is to provide a means of synthesizing and channeling structured data using a hierarchical state system. The system supports reactive IO and serialization of its dynamic generator objects.

Let's throw around some use cases, there are an unlimited number.

Gentyl can generate:
  • events, worlds and enemies within games.
  • language reflecting the structure of grammar.
  • Test Cases in an Automated testing program.
  • music and art with WebGL and Web Audio.
  • solutions in an evolutionary computation algorithm.
  • posts to appear in a feed.
  • Whatever.. the list will grow!
Gentyl can also:
  • Destructure
  • Compose
  • Store
  • Stream processing
  • Asynchonous input-output

Gentyl is:

  • data focused model of execution
  • dynamically typed
  • built with typescript
  • runnable on any javascript platform
  • tested with jasmine
  • process hierarchy


the fundamental unit is a G-node.

a G-node represents a generator object that can be resolved to produce objects.

G-nodes have 3 parts:

  • Crown: | the compositional tree structure of what is generated.
  • Form: | a description of how the node behaves, through Tractors and other properties.
  • Context: | the items of state modified by and informing the results of Tractors. Appears as this.

each part is represented by an object argument

an example G-node used to generate cats with eventual exhaustion of names could look like this.

var gentyl = require("gentyl")
var G = gentyl.G

var catGen = G({
            r:function(obj, args){
                return this.names.pop() || "puss"
            names:["Ben", "Lucy", "Magnus","Tiger"]    //refers to here
    r:function(obj, args){
        return `a size ${obj.size} cat with ${obj.ferocity} ferocity called ${}`

catGen.resolve() // -> a size 34 cat with 10 ferocity called Tiger
catGen.resolve() // -> a size 34 cat with 10 ferocity called Magnus

although it is not necessary to call prepare() before resolve; it will call it internally if it is left out, it is an important stage in the setup of the node


G-nodes contain a map, array, or value that contain/is G-nodes and primitives. The recursive architecture means static values in the tree can be converted to dynamic generators at a whim, as seen with ferocity becoming an increasing function of 'intensity'. The functions can gain access to their parent node's state using context association mode strings =+ (track parent) or =_ (track root). Here 'intensity' is a root property that determines both size and ferocity.

function Linear(obj){
    return obj.c + obj.m*this.intensity  //This A

function randIntRng(obj){
    return Math.floor(obj.min + Math.random()*(obj.max - obj.min))   

var NamePicker = G({},
        r:function(obj, args){
            return this.names.pop() || "puss" // This B
        names:["Ben", "Lucy", "Magnus","Tiger"]    //B refers to here

var catGen = G({
    size:G({m:2, c:10}, {r:Linear, m:"=+"}),
            r:Linear,  //this A within
        r:function(obj, args){
            this.intensity += 1; //This A
            return `a size ${obj.size} cat with ${obj.ferocity} ferocity called ${}`
       intensity:0  //This A refers to here

This G-Node will create cats like above with names like above but with sizes increasing in sequence (10, 12, 14..) and ferocity increasing but also with random influence, could be (6, 12, 9). Notice how easily nodes can be refactored the 'NamePicker' here can be used elsewhere without interfering with the state.


the following will outline the advanced features, explaining the concepts and components involved. For detailed examples please refer to the jasmine specs.

Sharing State

Gentyl is caring about state, it doesn't marginalise it like other functional reactive technologies. State is controlled by localising it to each node and constructing a special this object. Children are able to access and modify the parent state by explicitly saying they will, but not the other way around, thus the state dependency structure matches the containment structure. There are three access modes and three ways to designate the source.

State Construction Modes.

Mode Symbol Mnemonic Meaning
Track = The train tracks No modification only awareness of changes.
Share & And we will work together Child can modify the designated source.
Inherit (pipe) It's you or me The parent state is prototype/superclass.


type symbol Mnemonic meaning
parent + up by one designate the direct parent.
root _ at the bottom designate the root of the structure.
arbitrary x back up x stops designate x steps up the parent chain.

for example ' &+ ' means share parent, ' |_ ' means inherit from the root. ' =2 ' means track my parents parent. These can be combined but there are rules about conflicting names and not sharing.


At the heart of the execution model, doing all the heavy lifting, are Tractors. These are functions that are bound to the constructed context object of the G-node, that are called at some point in the execution of a G-node. They are called tractors because of how they form a 'tract' through a context from argument to return which is able to disturb the state within. They are always provided as unbound functions, typically not closures, they refer to the context through this. Tractors are always provided as a single letter property of the form object. The only possible tractors are currently resolver, carrier, selector and preparator at the core and injector, ejector and targeter in Gentyl's IO system.

Function Symbol Arguments Returns Role
Preparator p 1:initialization arguments passed to prepare() and replicate() void works as a constructor for the state of the generator
Carrier c 1: the argument passed from the parent or resolve() of the root the argument passed to the components appropriation, elaborating, preparation.
Selector s 1: the argument returned from the carrier, 2:the full set of keys or indicies as array the iterable with the indicies or keys of children that will resolve filtering, execution, selective dispatch.
Resolver r 1: The resolved component object, 2:the value passed to resolve this node value passed back to the parent composition, interpretation, filtering data.
Injector(input) i 1:the input argument if is root, the value passed to the root resolve for the input event. otherwise unused the function is for feeding data arriving asynchronously into the system
Ejector(output) o The resolve return value for the associated G-node The value that is passed to the output dispatching function output preprocessing, extraction, representation
Targeter t 1: the input argument A string or array of strings that is the output labels of G-nodes that will execute when this node recieves input selective dispatch, output filtering


These are the methods of the G-Node object, most of them perform recursively to capture the whole structure.

Name Purpose
prepare(prepArgs) This method transforms the tree of unprepared g-node, creating parent links and constructing the state dependencies. It is a necessary operation before resolve otherwise the functions would not be able to access the wider context.
resolve(resArgs) -> result Take the resolve args recursively working out to the leaf nodes through carrier and selector and back through the resolver. This is the fundamental generative action the running of the core algorithm.
replicate(prepArgs) -> G-Node Produce a copy of the original node(called the ancestor), replicas have isolated state but share their form. The relationship between ancestor, replicate and the resultant gnode is much like a prototype, new keyword and the instance. Therefore G-Nodes are their own prototype.
bundle()-> G-bundle This action is a serialization of the G-node structure that can be converted to JSON and recovered later, so long as a means of recovering the functions within the form is provided(currently only recoverable within the required module instance). it is recoverable by calling R(G-bundle).
shell() -> SignalShell This is the io mechanism of gentyl. A signal shell is an object with ins and outs: {ins:{label1:inpFunction1, label2:inpFunction2... }, outs:{label1:outputSignal1, label2:outputSignal2}}. calling the input function for a label calls functions that inject data into the system . The output signals can have callbacks attached to them, these will be called when the node with the corresponding output label is resolved.



Bundling and Reconstruction