Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


formform is a modular JavaScript library all about the 4-valued logic of cognition first introduced 2017 by Ralf Peyn in uFORM iFORM. In its core, the purpose of the library is to calculate with all 5 FORMs (marked, unmarked, undetermined, imaginary and unclear) introduced in the book and is meant to be extended with more specialized modules for different tasks such as FORM representation, algebra, visualization or simulation and analysis using CAs.

As a helpful tool for researchers and enthusiasts and as a demonstration of the library's capabilities I have also created the FORM tricorder. It can calculate, represent and visualize FORMs using my special formula syntax (described below under formform.form). Further applications (like a cellular automaton) are listed on the formform website.

Please note that my library as well as my apps are still work in progress. The library is currently in the process of restructuring and you may want to wait for a more stable release if you intent to use it in your projects. Although I am very passionate about this, I am not a formally trained developer and cannot yet afford to do this full-time.


npm install formform
// ES6:
import formform from 'formform';

// CommonJS:
let formform = require('formform');

Or you can just embed the library via script-tag:

<script src="<yourpath>/formform.min.js"></script>
// or just use the unpkg link:
<script src=""></script>

Documentation (outdated – update coming soon)

In its current state, formform has 4 classes that partly inherit from each other and perform different tasks.

  • formform.calc lets you calculate with numeric values of the uFORM iFORM calculus
  • formform.form lets you calculate any FORM with constants and variables using a special JSON representation which can be easily generated from a formula String (paranthesis notation)
  • formform.graph lets you use this JSON representation to generate different visualization outputs or notations
  • formform.dna lets you store calculation results in a code format I call formDNA and visualize them as vmaps to analyze value patterns

All classes and their API are described in detail below:


x,y,z,… → {0,1,2,3}

# Value
0 unmarked (n)
1 marked (m)
2 undetermined (u)
3 imaginary (i)

FORM arithmetic for commutative relation x y:

rel(...vars) // use any number of variables

FORM arithmetic for inversion/negation (x):

inv(x,n) // n -> number of inversions

FORM arithmetic for different self-equivalent re-entry FORMs:

reEntry(reEven, lastOpen, altInterpr, ...vars)
  • reEven: even re-entry-number?
  • lastOpen: last variable not crossed?
  • altInterpr: alternative interpretation (as described in uFORM iFORM on p.167)

Shortcuts for more complex FORM arithmetics:

uForm2(a,b, altInterpr=false)
iForm2(a,b, altInterpr=false)

uForm3(lastOpen, a,b,c, altInterpr=false)
iForm3(lastOpen, a,b,c, altInterpr=false)

uForm4(a,b,c,d, altInterpr=false)
iForm4(a,b,c,d, altInterpr=false)

uForm5(lastOpen, a,b,c,d,e altInterpr=false)
iForm5(lastOpen, a,b,c,d,e altInterpr=false)

FORM arithmetic for "implication":

implL(x,y) // (x)y
implR(x,y) // x(y)

FORM arithmetic for "presection" / "postsection":

pre(x,y)  // ((x)y)
post(x,y) // (x(y))

FORM arithmetic for "contravalence" / "equivalence":

cont(x,y)  // ((x)y) (x(y))
equiv(x,y) // ( ((x)y) (x(y)) )


form -> either a formula or a JSON-representation of the FORM

formula -> String in paranthesis-notation (see below for the syntax)

varorder -> array of the variable interpretation order of a form, specified as [x,…] (where x is a variable)

Introduction to formula syntax

Enclosing FORMs are typed as parenthesis (…), constants as numbers (0,1,2,3 see formform.calc above), variables as either single characters: a,b,c,… or labels, using quotes: "Ball", "door is open", "3×2=12", …) and unclear forms are enclosed in slashes: /God/,/World Peace/,… .

Self-equivalent re-entry forms are marked by curly brackets {…} and their variables/constants/FORMs are separated by commas {a,b,…}. Please note that the order of the variables is: {deepest nesting → shallowest nesting}. Using pipes before the arguments, you can also specify open forms (without the outer cross) {open|a,…} and either even {2r|a,…} or odd {2r+1|a,…} re-entry numbers as well as combinations {2r|open|a,…}. You can also switch to the alternative interpretation using the keyword "alt", like so: {2r|alt|a,b} (order of keywords is irrelevant).

Recursive form calculation


Interpretation of variables in a form

interpret(form, interpr)
  • interpr: Array of values corresponding to each variable in the form

Interpretation followed by calculation:

interCalc(form, interpr)

The “Master-function”

Interpretation and calculation of all possible values of the form:


JSON-representation of a FORM from its formula

Convert from formula into JSON string


Convert and parse formula as JSON object:



Get all variables of a form as an Array:


Get a JSON-String representation of a form:


Traverse all FORM-types (without variables/values) in a form and apply a callback function:

traverseForm(form, function(fBranch) {
  // ...
  • fBranch: current FORM-branch of the FORM

Get total number of variables from a formula:


Re-order/permute variables in a formula to match the order given in varorder:

reOrderVars(formula, varorder)


You can learn more about the visualization types if you take a look at my FORM tricorder and click on "show explanations", I will not go into more detail in this documentation.

Visualization (D3/SVG)

createGraph(graphType, form, options)

options ->

  parentId: <id>            // id of parent DOM-element (default: parent is 'body')
  width: <px>               // width of the container (default: container auto-fits to chart)
  height: <px>              // height of the container (default: container auto-fits to chart)
  margin: {left: <px>, right: <px>, top: <px>, bottom: <px>}
                            // margin of the container (default: {50,50,50,50})
  padding: {left: <px>, right: <px>, top: <px>, bottom: <px>}
                            // padding of the container (default: {10,10,10,10})
  styleClass: style         // ('pack' only) style of the graph (see above for available values)
  drawBackground: <boolean> // attaches a white background element to the svg
  compactChecked: <boolean> // ('gsbhooks' only) switches to a more compact style for reEntry FORMs


A note about representation

The graph module expands on the JSON-representation of the form module by constructing nested re-entry FORMs from its more compact descriptive structure. This is necessary for the accurate visualization of those FORMs but the form module didn't need to be so explicit since I took advantage of their common patterns in the calculation algorithm.

If you need to expand the JSON-structure of re-entry FORMs yourself, use: expand_reEntry(form)


formDNA -> string in the form ::dna, formula::dna or formula.varorder::dna

dna -> string in quaternary number format representing each interpretation value (of a value table for an equivalence class of possibly infinite FORMs) in reverse order

vmap -> visual representation of formDNA as a recursive variable/value map

vmap perspective -> permutation of a vmap by permutation of its variable interpretation order

Conversion to and from formDNA

formformDNA, retaining formula and (optional) varorder:

formToDNA(input, varorder, options)

Specify output type in the options object as either html, text or num (just the dna as a quaternary number string).

formDNAform using an optional varorder: (experimental)

dnaToFORM(formDNA, varorder, options)

integer → dna (use BigInt(n) for numbers larger than 253 - 1):

intToDNA(int, vnum)

If no vnum is specified, the smallest variable number possible for the quaternary is assumed

Generation of formDNA

Random dna from a given variable number:


Generation of vmaps

vmap from form/formDNA input using an optional varorder:

vmap(input, varorder, options)

Generates an object containing the vmap tree (data structure) as well as an SVG element of the standard visualization.

options ->

  size:    <px>             // size of each value cell (default: '14' (-1 per new variable))
  gapGrow: <px>             // additional gap between value cells for each recursive step (default: '1.5')
  svgPad:  <px>             // padding around the vmap svg element (default: '0')

  strokeW: <px>             // width of border around each value cell (default: '0.5')
  strokeC: <css color>      // color of border around each value cell (default: '#fff')
  bgC:     <css color>      // color of background for the vmap container (default: '#fff')

  hideInputLabel: <boolean> // hides input (form/formDNA) label below vmap
  hideOrderLabel: <boolean> // hides variable order label below vmap
  fullInputLabel: <boolean> // prevents trimming if input label is too large (def.: false)
  inputLabelMax: <int>      // max number of characters in input label (form input only, def.: 200)
  customLabel: <String>     // specification of custom label below vmap

Set of all vmap perspectives (SVG format) of a form/formDNA input using an optional varorder:

vmapPerspectives(form, varorder, globalOptions)

globalOptions ->

  // -> integrates all options from vmap() and applies them to every vmap instance
  margin:  <px> // margin between vmap instances

List of vmaps (SVG format) from an array of form/formDNA input:

vmapList(inputList, globalOptions=undefined)

Syntax of inputList: [[input, varorder, options], …] list of lists of vmap() arguments

globalOptions ->

  // -> integrates all options from vmap() and applies them to every vmap instance
  // -> can be overwritten by local options of a vmap definition
  margin: <px>                // margin between vmap instances
  vAlign: <top|center|bottom> // vertical alignment of vmap instances

Further information

If you want to learn more about the calculus, ideas and theories formform is based on, here are some helpful resources:

  • About uFORM iFORM (mostly German as is the language of the book by Ralf Peyn, but you can try DeepL to translate the gist of it)
  • Here is a list of links on the theoretical background behind uFORM iFORM (mostly German resources, but you can just translate the keywords and google them)
  • 3-dimensional FORM animations and FORM-builder (a project I made back in 2017 that greatly influenced my approach to formform)
  • Blog s y s t e m z e i t by Gitta Peyn – German and English articles about systemic research based on uFORM iFORM
  • About FORMWELT – a coding language for language and meaning founded on the logic of cognition introduced in uFORM iFORM (by the way, we appreciate any support for the development of FORMWELT Online!)


This library has become my personal project since I first began studying uFORM iFORM, published by Ralf Peyn in 2017. Ralf's “SelFis” (visual interpretation of a partial System of the self-referential System of the FORM) inspired me to develop my own cellular automaton in the programming environment Processing to dig deeper and gain a fuller unterstanding of these systems.

Working with lookup-tables for FORM calculations was okay for a while, but also very tedious and impractical for my research, so I began working on some functions that would do the calculations for me. In 2018 I was finally able to implement an algorithm to calculate all self-equivalent re-entry FORMs as described in uFORM iFORM. I immediately did countless calculations by hand and let Ralf also check that the algorithm is solid and its results are correct.

As soon as I was able to automate calculation with undetermined FORMs, I saw that there was much more potential in this and that it could be very helpful for other people who want to work with FORM logic as well. So I began working on a JavaScript library to elaborate my ideas, which gradually became formform. Since its early development, formform has always evolved in a fruitful interplay with the applications built on top of it.

A first application that I have developed in parallel from the beginning was the FORM tricorder – a swiss army knife for FORM calculation, representation and visualization. In September 2019 I was finally able to develop a new cellular automaton for FORM logic SelFis with formform that is much more user-friendly and much more versatile than what I have done two years earlier. My experimentation with rule extraction by bitmasking in CAs led me to a code format I call formDNA that is an abstraction of the value table. It not only made my CA faster and more flexible, it also inspired me to create the vmap: a recursive variable/value map to visualize formDNA, that has great potential for pattern recognition in FORMs.

Driven by my own curiosity and some helpful suggestions from users, I continuously work on implementing new ideas and features into the library. Nowadays, I am prototyping most of these ideas in my Observable notebooks and announce new developments, bugfixes and changes on my Twitter account.

In the near future I want to rewrite my CA for SelFis as a more professional standalone application and also develop other CAs for decisionFORMs, lifeFORMs and mindFORMs. I also want to find a way to algorithmically generate spirals for circular re-entry FORMs, to have a more iconic representation closer to Ralfs notation in uFORM iFORM. There are many more ideas in the pipeline, that I hope to realize as time and other resources allow.


If you want to support my work, consider buying me a coffee.

(c) 2018–2021 by Peter Hofmann

License: MIT


Modular JS-Library to calculate, visualize and (soon) simulate all the FORMs of uFORM iFORM





No releases published


No packages published