Skip to content

Latest commit

 

History

History
313 lines (226 loc) · 12.2 KB

README.md

File metadata and controls

313 lines (226 loc) · 12.2 KB

RPD — Reactive Pure Data

logo

Version 0.1.0

A video of the engine in action, demonstrates most of its features: [ Watch ].

Watch

Play online: [ Core Toolkit ] | [ PD Toolkit ] | [ Animatron Toolkit ]

(NB: Only modern browsers are supported, tested in Chrome and Safari, no mobile support for now)


Contents


Intro

RPD is a super-minimal plugin-based JS-driven engine for Node-Based User Interfaces — the ones like Pure Data, Quartz Composer, Reaktor, NodeBox, VVVV or any Shader/Material Composing View in your favorite 3D Editor.

And when I say minimal, I really mean it:

  • The engine code takes 560 lines of pure JS code with comments and stuff, it is 2.48KB closure-compiled and gzipped;
  • The HTML renderer is 1200 lines or so, due to masses of DOM manipulation code, and anyway it is 3.9KB when compiled;
  • Kefir.js, required for Reactive Streams support, is ~7KB when gzipped and compiled, since it is also targeted as minimal;

Together it takes only 13-14KB when compiled and gzipped, but still provides tons (!) of aweseome features!

Moreover, it's built with the help of Reactive programming (thanks to Kefir.js), and this way it allows a programmer to treat and process any data flow as a stream, so:

colorInlet.stream(Kefir.repeatedly(500, ['red', 'navy']));

Will send red and navy values every 500ms to a single color-value inlet in order. It's not the only feature which is available with streams, of course, see below for much more.

Here are some GIFs in reduced quality, in addition to a video in rather good quality above, to help you decide if it worths to use this engine or not (also please take a look at code examples below!).

Core GIF PD GIF Animatron GIF

The Engine API provides easy ways to program node networks. Or to define a custom node or a channel type. Even node sets (named toolkits) are enormously easy to build!

Let's switch to some simple examples. Detailed stuff is under the links below.

Constructing a network of nodes:

var first = new Rpd.Node('core/custom', 'Test');
var boolOutlet = first.addOutlet('core/bool', true);
first.addOutlet('core/number', 1);
first.addOutlet('core/number');

var second = new Rpd.Node('core/custom', 'Foo');
var boolInlet = second.addInlet('core/boolean');
var numInlet = second.addInlet('core/number');

boolOutlet.connect(boolInlet);
boolOutlet.connect(numInlet, function(val) { return (val === true) ? 1 : 0 });
boolOutlet.send(false);
boolOutlet.stream(Kefir.repeatedly(10, [true, false]));

Creating custom node types is very easy:

Rpd.nodetype('core/sum-of-three-with-body', {
    name: 'Sum of Three w/Body',
    inlets: {
        'a': { type: 'core/number', name: 'A', default: 1 },
        'b': { type: 'core/number', name: 'B' },
        'c': { type: 'core/number', name: 'C', hidden: true }
    },
    outlets: {
        'sum': { type: 'core/number', name: '∑' }
    },
    process: function(inlets) {
        return { 'sum': (inlets.a || 0) + (inlets.b || 0) + (inlets.c || 0) };
    }
});

Even very complex ones:

Rpd.nodetype('pd/play', function() {
    var lastSound;
    return {
        name: 'play',
        inlets: { 'sound': { type: 'pd/t-obj', default: null } },
        tune: function(updates) { return updates.throttle(50); },
        process: function(inlets, inlets_prev) {
            if (inlets_prev.sound) inlets_prev.sound.pause();
            if (inlets.sound) {
                lastSound = inlets.sound;
                inlets.sound.play();
            }
        },
        handle: {
            'node/turn-off': function() {
                if (lastSound) lastSound.pause();
            }
        }
    }
});

Here's the engine code at a glance;

Features

RPD provides following features:

  • User may observe nodes, manipulate nodes, connect inlets and outlets, effect is seen immediately; User may edit values on inlets, see results inside node bodies or additionally configure them there;
  • Network model may be stored in a simple JS File;
  • Developer may build custom node Toolkits to let user re-use them, in a very easy way; And it's not only restricted with configuring inlets and outlets—actually, every aspect of the node or a channel is configurable;
  • Streams provide developer with an unlimited power in sending, queueing, filtering, mapping/reducing/flattening, packing and un-packing any data, basing on time periods or not; every aspect from Reactive Programming may be used to operate data streams;
  • Plugin system allows to easily add renderers (HTML is provided, SVG and Canvas renderers are planned) or importers/exporters for specific Toolkits;
  • HTML renderer does not uses any style injection except some very minor cases, it only operates CSS classes, and it means you may completely redesign the look of your interface using only CSS;
  • Developer is free to use _any helper library _(while RPD tries to use only Kefir and nothing else), and it is very easy: i.e. HTML module may easily be replaced with some jQuery analogue.
  • Node model may be easily programmed and updated on-the-fly (i.e. while nodes already send some data);
  • Node model has no side-effects in functional meaning, every change or update is provided through event streams, no data is stored or changed (expect construction); plugins, on the other hand, are completely free to use any programming model they prefer, and they are actually written in much more imperative style than the Engine;
  • It is so easy to code for RPD, I hope community will be able and happy to write new toolkits, renderers and importers and help evolving the engine;

Planned Features

  • PureData and/or Animatron node Toolkits as an examples out-of-the-box (cover more abilities);
  • Support Procedures (re-use node sequences by name);
  • SVG and Canvas renderers;
  • Pure-Data-compatible Import/Export or some special format;
  • Infinite Node Workspace;
  • Ability to be injected into any part of a page;
  • Smarter layouting;
  • Support mobile browsers;

More may be seen in Issues section.

Using

Download kefir.min.js.

Choose a distribution of RPD and download it:

If your choise of renderer is HTML, get a corresponding CSS file:

Add these files to a head of your page:

For Core Toolkit only:

<script src="./kefir.min.js"></script>
<script src="./rpd-core-html.min.js"></script>
<link rel="stylesheet" href="./rpd-core.css"></link>

For Core & PD Toolkits:

Download timbre.js.

<script src="./kefir.min.js"></script>
<script src="./timbre.js"></script>
<script src="./rpd-core-pd-html.min.js"></script>
<link rel="stylesheet" href="./rpd-core-pd.css"></link>

For Core & Animatron Toolkits:

Download anm-player.min.js.

<script src="./kefir.min.js"></script>
<script src="./anm-player.min.js"></script>
<script src="./rpd-core-anm-html.min.js"></script>
<link rel="stylesheet" href="./rpd-core-anm.css"></link>

Now, you may just initialize user model and let him/her add nodes by himself/herself:

var model = Rpd.Model.start().attachTo(document.body)
                             .renderWith('html');

Or, you may add some prepared nodes after that (you may save them to a file, of course):

var first = new Rpd.Node('core/custom', 'Test');
var boolOutlet = first.addOutlet('core/bool', true);
first.addOutlet('core/number', 1);
first.addOutlet('core/number');

var second = new Rpd.Node('core/custom', 'Foo');
var boolInlet = second.addInlet('core/boolean');
var numInlet = second.addInlet('core/number');

boolOutlet.connect(boolInlet);
boolOutlet.connect(numInlet, function(val) { return (val === true) ? 1 : 0 });
boolOutlet.send(false);
boolOutlet.stream(Kefir.repeatedly(10, [true, false]));

Participating

To participate, get a copy of repository:

git clone git@github.com:shamansir/rpd.git

After that, get dependencies:

make deps

(Updating dependencies may be performed only once a version of RPD is increased, it is not required to get them before every build.)

Now you should be able to run examples from ./examples/*.html.

To minify and join sources (prepare for distribution), ensure you have Closure Compiler installed, put (or link to) compiler.jar in the root directory of RPD, and then run, for HTML renderer case:

make dist-html

Or, to build a version with PD Toolkit included and HTML renderer, run:

make dist-pd-html

To build with Animatron toolkit, for example, just replace -pd- with -anm-.

You'll find the results under ./dist folder.

Feel free to fix issues or do Pull Requests!

See a Reference below for details in programming Tollkits and different other things.

Reference

Cheatsheets:

Toolkits:

Class Reference: