Created
April 22, 2019 19:10
-
-
Save abachman/283a86fef033f8929da6255729a5daa6 to your computer and use it in GitHub Desktop.
MICAVIBE style p5.js coding environment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script> | |
<script src="support.js"></script> | |
<script src="sketch.js"></script> | |
<script> | |
$(function () { | |
window.WS_URL = 'ws://micavibe.com/streaming' | |
startWebsocket() | |
window.sketch(document.getElementById('container')) | |
}) | |
</script> | |
</head> | |
<body> | |
<div id="container" style="width:100%; height:640px;"></div> | |
</body> | |
</html> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HANDY LINKS: | |
// | |
// p5.js -> https://p5js.org/reference/ | |
// javascript -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference | |
// micavibe -> http://micavibe.com | |
// adafruit IO -> https://io.adafruit.com/mica_ia/public | |
// | |
window.sketch = createProcessingSketch('*', function (self, p) { | |
/* | |
* | |
* Make changes inside this function | |
* | |
* All processing functions should be prefixed with `p.` | |
* | |
* If you want to receive data from a single station, use 'sound', 'sound2', | |
* 'split-motion', or 'mood' in place of '*' above. | |
* | |
*/ | |
var record = null | |
var moods = [] | |
// | |
// p5.js drawing code. The default functions every sketch should define are p.setup and p.draw. | |
// | |
p.setup = function () { | |
// the `self` record has the initial sketch dimensions. To change the size | |
// of the canvas on the page, see index.html and the <div> element with id="container" | |
p.createCanvas(self.width, self.height); | |
p.noStroke(); | |
// See the `onData` function below for an example of handling live records | |
// OR use the `getData` function to make a request when the sketch starts. | |
p.getData('mood', 10).then(function (records) { | |
moods = [] | |
records. | |
map(function (datum) { | |
// splitting records | |
return datum.value.split(' ') | |
}). | |
map(function (values) { | |
// converting values to numbers | |
return values.map(function (value) { return parseInt(value) }) | |
}). | |
forEach(function (values) { | |
// finally building a single list of values | |
moods = moods.concat(values) | |
}) | |
console.log("GOT MOOD RECORDS:", moods) | |
}) | |
} | |
p.draw = function () { | |
p.background(0) | |
if (record) { | |
p.fill(255) | |
p.text(JSON.stringify(record, null, ' '), 10, 20) | |
} | |
} | |
// This function is called every time new data is received. | |
p.onData = function (data) { | |
// data.value for all stations is: | |
// * a string | |
// * containing one or more numbers | |
// * separated by spaces | |
// | |
// what the value represents depends on the station | |
// | |
// we can get at the actual number values like this: | |
var values = data.value. | |
// splitting values | |
split(' '). | |
// converting to numbers | |
map(function (value) { return parseInt(value) }) | |
// now we have a list of numbers for the `data.key` feed | |
record = { | |
key: data.key, | |
values: values | |
} | |
} | |
}) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// EventEmitter3 | |
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).EventEmitter3=e()}}(function(){return function i(s,f,c){function u(t,e){if(!f[t]){if(!s[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(a)return a(t,!0);var r=new Error("Cannot find module '"+t+"'");throw r.code="MODULE_NOT_FOUND",r}var o=f[t]={exports:{}};s[t][0].call(o.exports,function(e){return u(s[t][1][e]||e)},o,o.exports,i,s,f,c)}return f[t].exports}for(var a="function"==typeof require&&require,e=0;e<c.length;e++)u(c[e]);return u}({1:[function(e,t,n){"use strict";var r=Object.prototype.hasOwnProperty,v="~";function o(){}function f(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function i(e,t,n,r,o){if("function"!=typeof n)throw new TypeError("The listener must be a function");var i=new f(n,r||e,o),s=v?v+t:t;return e._events[s]?e._events[s].fn?e._events[s]=[e._events[s],i]:e._events[s].push(i):(e._events[s]=i,e._eventsCount++),e}function u(e,t){0==--e._eventsCount?e._events=new o:delete e._events[t]}function s(){this._events=new o,this._eventsCount=0}Object.create&&(o.prototype=Object.create(null),(new o).__proto__||(v=!1)),s.prototype.eventNames=function(){var e,t,n=[];if(0===this._eventsCount)return n;for(t in e=this._events)r.call(e,t)&&n.push(v?t.slice(1):t);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(e)):n},s.prototype.listeners=function(e){var t=v?v+e:e,n=this._events[t];if(!n)return[];if(n.fn)return[n.fn];for(var r=0,o=n.length,i=new Array(o);r<o;r++)i[r]=n[r].fn;return i},s.prototype.listenerCount=function(e){var t=v?v+e:e,n=this._events[t];return n?n.fn?1:n.length:0},s.prototype.emit=function(e,t,n,r,o,i){var s=v?v+e:e;if(!this._events[s])return!1;var f,c,u=this._events[s],a=arguments.length;if(u.fn){switch(u.once&&this.removeListener(e,u.fn,void 0,!0),a){case 1:return u.fn.call(u.context),!0;case 2:return u.fn.call(u.context,t),!0;case 3:return u.fn.call(u.context,t,n),!0;case 4:return u.fn.call(u.context,t,n,r),!0;case 5:return u.fn.call(u.context,t,n,r,o),!0;case 6:return u.fn.call(u.context,t,n,r,o,i),!0}for(c=1,f=new Array(a-1);c<a;c++)f[c-1]=arguments[c];u.fn.apply(u.context,f)}else{var l,p=u.length;for(c=0;c<p;c++)switch(u[c].once&&this.removeListener(e,u[c].fn,void 0,!0),a){case 1:u[c].fn.call(u[c].context);break;case 2:u[c].fn.call(u[c].context,t);break;case 3:u[c].fn.call(u[c].context,t,n);break;case 4:u[c].fn.call(u[c].context,t,n,r);break;default:if(!f)for(l=1,f=new Array(a-1);l<a;l++)f[l-1]=arguments[l];u[c].fn.apply(u[c].context,f)}}return!0},s.prototype.on=function(e,t,n){return i(this,e,t,n,!1)},s.prototype.once=function(e,t,n){return i(this,e,t,n,!0)},s.prototype.removeListener=function(e,t,n,r){var o=v?v+e:e;if(!this._events[o])return this;if(!t)return u(this,o),this;var i=this._events[o];if(i.fn)i.fn!==t||r&&!i.once||n&&i.context!==n||u(this,o);else{for(var s=0,f=[],c=i.length;s<c;s++)(i[s].fn!==t||r&&!i[s].once||n&&i[s].context!==n)&&f.push(i[s]);f.length?this._events[o]=1===f.length?f[0]:f:u(this,o)}return this},s.prototype.removeAllListeners=function(e){var t;return e?(t=v?v+e:e,this._events[t]&&u(this,t)):(this._events=new o,this._eventsCount=0),this},s.prototype.off=s.prototype.removeListener,s.prototype.addListener=s.prototype.on,s.prefixed=v,s.EventEmitter=s,void 0!==t&&(t.exports=s)},{}]},{},[1])(1)}); | |
/// Websocket Connection | |
window.DEBUG = false | |
function WebSocketClient() { | |
this.reconnect_interval = 1500; | |
} | |
WebSocketClient.prototype.open = function (url) { | |
this.url = url | |
this.instance = new WebSocket(this.url); | |
var self = this | |
this.instance.onopen = function () { | |
if (window.DEBUG) | |
console.log("[WebSocketClient on open]") | |
self.onopen() | |
} | |
this.instance.onclose = function (evt) { | |
if (window.DEBUG) | |
console.log("[WebSocketClient on close]") | |
switch (evt.code){ | |
case 1000: // CLOSE_NORMAL | |
if (window.DEBUG) | |
console.log("WebSocketClient: closed"); | |
break; | |
default: // Abnormal closure | |
self.reconnect(evt); | |
break; | |
} | |
if (self.onclose) | |
self.onclose(evt); | |
} | |
this.instance.onerror = function (evt) { | |
if (window.DEBUG) | |
console.log("[WebSocketClient on error]") | |
switch (evt.code){ | |
case 'ECONNREFUSED': | |
self.reconnect(evt); | |
break; | |
default: | |
if (self.onerror) self.onerror(evt); | |
break; | |
} | |
} | |
this.instance.onmessage = function (evt) { | |
if (window.DEBUG) | |
console.log("[WebSocketClient on message]") | |
self.onmessage(evt.data) | |
} | |
if (window.DEBUG) | |
console.log("[WebSocketClient open] completed") | |
} | |
WebSocketClient.prototype.removeAllListeners = function () { | |
this.instance.onopen = null | |
this.instance.onclose = null | |
this.instance.onerror = null | |
this.instance.onmessage = null | |
} | |
WebSocketClient.prototype.reconnect = function (evt) { | |
if (window.DEBUG) | |
console.log("WebSocketClient: retry in", this.reconnect_interval, "ms", evt); | |
this.removeAllListeners(); | |
var self = this | |
setTimeout(function() { | |
if (window.DEBUG) | |
console.log("WebSocketClient: reconnecting...") | |
self.open(self.url) | |
}, this.reconnect_interval) | |
} | |
window.Socket = new EventEmitter3() | |
function startWebsocket(callback) { | |
var sock = new WebSocketClient() | |
sock.open(window.WS_URL) | |
sock.onopen = function (event) { | |
console.log("socket connected") | |
} | |
sock.onclose = function () { | |
console.log("socket closed") | |
} | |
sock.onerror = sock.onclose | |
sock.onmessage = function (data) { | |
if (data instanceof Blob) { | |
var reader = new FileReader() | |
reader.onload = function () { | |
var message = JSON.parse(reader.result) | |
if (callback) { | |
callback(message) | |
} else { | |
window.Socket.emit('data.*', message) | |
if (message.key) { | |
window.Socket.emit('data.' + message.key, { | |
key: message.key, | |
value: message.value, | |
mode: message.mode ? message.mode : 'live' | |
}) | |
} else { | |
window.Socket.emit('data', message) | |
} | |
} | |
} | |
reader.readAsText(data) | |
} else if (typeof data == "string") { | |
var message = JSON.parse(data) | |
if (callback) { | |
callback(message) | |
} else { | |
window.Socket.emit('data.*', message) | |
if (message.key) { | |
window.Socket.emit('data.' + message.key, { | |
key: message.key, | |
value: message.value, | |
mode: message.mode ? message.mode : 'live' | |
}) | |
} else { | |
window.Socket.emit('data', { message: message }) | |
} | |
} | |
} | |
} | |
} | |
/// Sketch Creation | |
if (!window.sketches) { | |
window.sketches = {} | |
} | |
function createProcessingSketch(channel, callback) { | |
return function (container) { | |
// console.log("[createProcessingSketch] got container") | |
var sketch = function (p) { | |
var self = this | |
var resize = function () { | |
self.width = $(container).width() | |
self.height = $(container).height() | |
} | |
resize() | |
var last_call = null | |
// this function returns a Promise that serves historical data | |
p.getData = function (feed_key, limit=25) { | |
var now = new Date().getTime() | |
if (last_call && now - last_call < 5000) { | |
console.log("slow down......") | |
return Promise.resolve([]) | |
} | |
console.log("requesting") | |
last_call = now | |
return fetch("https://io.adafruit.com/api/v2/mica_ia/feeds/"+ feed_key + "/data?limit=" + limit).then(function (response) { | |
return response.json() | |
}) | |
} | |
// user defined callback includes only the actual Processing drawing code | |
callback(self, p) | |
p.windowResized = _.debounce(function () { | |
resize() | |
p.resizeCanvas(self.width, self.height); | |
}, 300) | |
// give the p5 sketch time to start before pumping data | |
setTimeout(function () { | |
var channels | |
if (!Array.isArray(channel)) { | |
channels = [ channel ] | |
} else { | |
channels = channel | |
} | |
channels.forEach(function (chan) { | |
window.Socket.on('data.' + chan, function (data) { | |
if (p.onData) { | |
p.onData(data) | |
} | |
}) | |
}) | |
}, 75) | |
} | |
return new p5(sketch, container) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment