Javascript
Vanilla JS
Type .js in the first line of an input cell and start writing Javascript on a new line.
Entered expressions are evaluated as anonymous function in the isolated environment scoping all declared variables. Depending on the returned result the output cell can show JSON string or DOM element.
.js
return 1+1 return 2; or
.js
const dom = document.createElement('span');
dom.innerText = "Hello World";
dom.style.color = 'lightblue';
return dom; const dom = document.createElement('span');
dom.innerText = "Hello World";
dom.style.color = 'lightblue';
return dom; You can still reach global variables with window or core objects
Cell context
There are a few useful built-in objects accessible from the top scope of called expressions:
this.ondestroy
This property is called when a cell is destroyed. Assign a clean-up function to it:
.js
this.ondestroy = () => {
// Clean up resources
} Always clean up any timers or animation loops using the this.ondestroy property. Otherwise, they will continue running even after the cell is reevaluated.
For example:
.js
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 250;
canvas.height = 250;
const ballRadius = 10;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
let uid;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
x += dx;
y += dy;
uid = requestAnimationFrame(draw);
}
uid = requestAnimationFrame(draw);
this.ondestroy = () => {
cancelAnimationFrame(uid);
};
return canvas; const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 250;
canvas.height = 250;
const ballRadius = 10;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
let uid;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
x += dx;
y += dy;
uid = requestAnimationFrame(draw);
}
uid = requestAnimationFrame(draw);
this.ondestroy = () => {
cancelAnimationFrame(uid);
};
return canvas;
Communication with the Wolfram Kernel
You can define any frontend symbol using JavaScript cells.
Frontend symbols
An overview on frontend symbols / functions
Custom input elements
How to use Javascirpt to make custom input elements and integrate them with Wolfram language
MJS Javascript
Type .mjs in the first line of an input cell and start writing Javascript on a new line. This type of Javascript cells allows to import modules and bundle them using Node.js to run in the output cells.
The main motication behind it is to take advantage of a vast NPM library, build a standalone script, which can be executed directly in the notebook and stored there as well.
This requires Node.js to be globally installed on your system.
Unlike Vanilla JS it does not return anything directly. Instead it provides a special property available from the content:
.mjs
const dom = document.createElement('span');
dom.style.color = "red";
dom.innerText = 'Hello World';
this.return(dom); const dom = document.createElement('span');
dom.style.color = "red";
dom.innerText = 'Hello World';
return dom; MJS cells use rollup bundler (installed automatically locally) and create packages.json locally in your notebook's folder.
Note, that the bundled output is stored within the notebook. You do not need to reevaluate the input cell. If you wish to copy the whole group to another notebook - use cell's properties (See Overview).
Cell context
There are a few useful built-in objects accessible from the top scope of called expressions:
this.ondestroy
This function is called when a cell is destroyed. Assign any clean-up function to this object
this.return
This is a function that returns a given object to the output cell.
this.after
This function is called when DOM element (if provided early via this.return) has been mounted. Use it to trigger any layout-dependent rendering functions
For example, let's install an NPM library using Shell type cell:
.sh
npm i siriwave --prefix . Use always --prefix . to force npm work locally
Now we can create a simple animation following the documentation of siriwave:
.mjs
import SiriWave from "siriwave";
const dom = document.createElement('div');
this.return(dom);
let siriWave;
siriWave = new SiriWave({
container: dom,
height: 300,
style: "ios9",
width: 600
});
siriWave.start();
this.ondestroy = () => {
siriWave.dispose();
console.warn('Removed');
} var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __commonJS=(cb,mod)=>function __require(){return mod||(0,cb[__getOwnPropNames(cb)[0]])((mod={exports:{}}).exports,mod),mod.exports};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))
if(!__hasOwnProp.call(to,key)&&key!==except)
__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}
return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod));var require_siriwave_umd=__commonJS({"node_modules/siriwave/dist/siriwave.umd.js"(exports,module){(function(factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.SiriWave=factory())})((function(){"use strict";var __assign=function(){__assign=Object.assign||function __assign2(t){for(var s,i=1,n=arguments.length;i<n;i++){s=arguments[i];for(var p in s)if(Object.prototype.hasOwnProperty.call(s,p))t[p]=s[p]}
return t};return __assign.apply(this,arguments)};function __rest(s,e){var t={};for(var p in s)if(Object.prototype.hasOwnProperty.call(s,p)&&e.indexOf(p)<0)
t[p]=s[p];if(s!=null&&typeof Object.getOwnPropertySymbols==="function")
for(var i=0,p=Object.getOwnPropertySymbols(s);i<p.length;i++){if(e.indexOf(p[i])<0&&Object.prototype.propertyIsEnumerable.call(s,p[i]))
t[p[i]]=s[p[i]]}
return t}
var ClassicCurve=((function(){function ClassicCurve2(ctrl,definition){this.ATT_FACTOR=4;this.GRAPH_X=2;this.AMPLITUDE_FACTOR=0.6;this.ctrl=ctrl;this.definition=definition}
ClassicCurve2.prototype.globalAttFn=function(x){return Math.pow(this.ATT_FACTOR/(this.ATT_FACTOR+Math.pow(x,this.ATT_FACTOR)),this.ATT_FACTOR)};ClassicCurve2.prototype.xPos=function(i){return this.ctrl.width*((i+this.GRAPH_X)/(this.GRAPH_X*2))};ClassicCurve2.prototype.yPos=function(i){return this.AMPLITUDE_FACTOR*(this.globalAttFn(i)*(this.ctrl.heightMax*this.ctrl.amplitude)*(1/this.definition.attenuation)*Math.sin(this.ctrl.opt.frequency*i-this.ctrl.phase))};ClassicCurve2.prototype.draw=function(){var ctx=this.ctrl.ctx;ctx.moveTo(0,0);ctx.beginPath();var finalColor=this.definition.color||this.ctrl.color;ctx.lineWidth=this.definition.lineWidth;for(var i=-this.GRAPH_X;i<=this.GRAPH_X;i+=this.ctrl.opt.pixelDepth){ctx.lineTo(this.xPos(i),this.ctrl.heightMax+this.yPos(i))}
ctx.stroke()};ClassicCurve2.getDefinition=function(){return[{attenuation:-2,lineWidth:1,opacity:0.1},{attenuation:-6,lineWidth:1,opacity:0.2},{attenuation:4,lineWidth:1,opacity:0.4},{attenuation:2,lineWidth:1,opacity:0.6},{attenuation:1,lineWidth:1.5,opacity:1}]};return ClassicCurve2})());var iOS9Curve=((function(){function iOS9Curve2(ctrl,definition){this.GRAPH_X=25;this.AMPLITUDE_FACTOR=0.8;this.SPEED_FACTOR=1;this.DEAD_PX=2;this.ATT_FACTOR=4;this.DESPAWN_FACTOR=0.02;this.DEFAULT_NOOFCURVES_RANGES=[2,5];this.DEFAULT_AMPLITUDE_RANGES=[0.3,1];this.DEFAULT_OFFSET_RANGES=[-3,3];this.DEFAULT_WIDTH_RANGES=[1,3];this.DEFAULT_SPEED_RANGES=[0.5,1];this.DEFAULT_DESPAWN_TIMEOUT_RANGES=[500,2e3];this.ctrl=ctrl;this.definition=definition;this.noOfCurves=0;this.spawnAt=0;this.prevMaxY=0;this.phases=[];this.offsets=[];this.speeds=[];this.finalAmplitudes=[];this.widths=[];this.amplitudes=[];this.despawnTimeouts=[];this.verses=[]}
iOS9Curve2.prototype.getRandomRange=function(e){return e[0]+Math.random()*(e[1]-e[0])};iOS9Curve2.prototype.spawnSingle=function(ci){var _a,_b,_c,_d,_e,_f,_g,_h,_j,_k;this.phases[ci]=0;this.amplitudes[ci]=0;this.despawnTimeouts[ci]=this.getRandomRange((_b=(_a=this.ctrl.opt.ranges)===null||_a===void 0?void 0:_a.despawnTimeout)!==null&&_b!==void 0?_b:this.DEFAULT_DESPAWN_TIMEOUT_RANGES);this.offsets[ci]=this.getRandomRange((_d=(_c=this.ctrl.opt.ranges)===null||_c===void 0?void 0:_c.offset)!==null&&_d!==void 0?_d:this.DEFAULT_OFFSET_RANGES);this.speeds[ci]=this.getRandomRange((_f=(_e=this.ctrl.opt.ranges)===null||_e===void 0?void 0:_e.speed)!==null&&_f!==void 0?_f:this.DEFAULT_SPEED_RANGES);this.finalAmplitudes[ci]=this.getRandomRange((_h=(_g=this.ctrl.opt.ranges)===null||_g===void 0?void 0:_g.amplitude)!==null&&_h!==void 0?_h:this.DEFAULT_AMPLITUDE_RANGES);this.widths[ci]=this.getRandomRange((_k=(_j=this.ctrl.opt.ranges)===null||_j===void 0?void 0:_j.width)!==null&&_k!==void 0?_k:this.DEFAULT_WIDTH_RANGES);this.verses[ci]=this.getRandomRange([-1,1])};iOS9Curve2.prototype.getEmptyArray=function(count){return new Array(count)};iOS9Curve2.prototype.spawn=function(){var _a,_b;this.spawnAt=Date.now();this.noOfCurves=Math.floor(this.getRandomRange((_b=(_a=this.ctrl.opt.ranges)===null||_a===void 0?void 0:_a.noOfCurves)!==null&&_b!==void 0?_b:this.DEFAULT_NOOFCURVES_RANGES));this.phases=this.getEmptyArray(this.noOfCurves);this.offsets=this.getEmptyArray(this.noOfCurves);this.speeds=this.getEmptyArray(this.noOfCurves);this.finalAmplitudes=this.getEmptyArray(this.noOfCurves);this.widths=this.getEmptyArray(this.noOfCurves);this.amplitudes=this.getEmptyArray(this.noOfCurves);this.despawnTimeouts=this.getEmptyArray(this.noOfCurves);this.verses=this.getEmptyArray(this.noOfCurves);for(var ci=0;ci<this.noOfCurves;ci++){this.spawnSingle(ci)}};iOS9Curve2.prototype.globalAttFn=function(x){return Math.pow(this.ATT_FACTOR/(this.ATT_FACTOR+Math.pow(x,2)),this.ATT_FACTOR)};iOS9Curve2.prototype.sin=function(x,phase){return Math.sin(x-phase)};iOS9Curve2.prototype.yRelativePos=function(i){var y=0;for(var ci=0;ci<this.noOfCurves;ci++){var t=4*(-1+ci/(this.noOfCurves-1)*2);t+=this.offsets[ci];var k=1/this.widths[ci];var x=i*k-t;y+=Math.abs(this.amplitudes[ci]*this.sin(this.verses[ci]*x,this.phases[ci])*this.globalAttFn(x))}
return y/this.noOfCurves};iOS9Curve2.prototype.yPos=function(i){return this.AMPLITUDE_FACTOR*this.ctrl.heightMax*this.ctrl.amplitude*this.yRelativePos(i)*this.globalAttFn(i/this.GRAPH_X*2)};iOS9Curve2.prototype.xPos=function(i){return this.ctrl.width*((i+this.GRAPH_X)/(this.GRAPH_X*2))};iOS9Curve2.prototype.drawSupportLine=function(){var ctx=this.ctrl.ctx;var coo=[0,this.ctrl.heightMax,this.ctrl.width,1];var gradient=ctx.createLinearGradient.apply(ctx,coo);gradient.addColorStop(0,"transparent");gradient.addColorStop(0.1,"rgba(255,255,255,.5)");gradient.addColorStop(1-0.1-0.1,"rgba(255,255,255,.5)");gradient.addColorStop(1,"transparent");ctx.fillStyle=gradient;ctx.fillRect.apply(ctx,coo)};iOS9Curve2.prototype.draw=function(){var ctx=this.ctrl.ctx;ctx.globalAlpha=0.7;ctx.globalCompositeOperation=this.ctrl.opt.globalCompositeOperation;if(this.spawnAt===0){this.spawn()}
if(this.definition.supportLine){return this.drawSupportLine()}
for(var ci=0;ci<this.noOfCurves;ci++){if(this.spawnAt+this.despawnTimeouts[ci]<=Date.now()){this.amplitudes[ci]-=this.DESPAWN_FACTOR}else{this.amplitudes[ci]+=this.DESPAWN_FACTOR}
this.amplitudes[ci]=Math.min(Math.max(this.amplitudes[ci],0),this.finalAmplitudes[ci]);this.phases[ci]=(this.phases[ci]+this.ctrl.speed*this.speeds[ci]*this.SPEED_FACTOR)%(2*Math.PI)}
var maxY=-Infinity;for(var _i=0,_a=[1,-1];_i<_a.length;_i++){var sign=_a[_i];ctx.beginPath();for(var i=-this.GRAPH_X;i<=this.GRAPH_X;i+=this.ctrl.opt.pixelDepth){var x=this.xPos(i);var y=this.yPos(i);ctx.lineTo(x,this.ctrl.heightMax-sign*y);maxY=Math.max(maxY,y)}
ctx.closePath();ctx.fillStyle="rgba(".concat(this.definition.color,", 1)");ctx.strokeStyle="rgba(".concat(this.definition.color,", 1)");ctx.fill()}
if(maxY<this.DEAD_PX&&this.prevMaxY>maxY){this.spawnAt=0}
this.prevMaxY=maxY;return null};iOS9Curve2.getDefinition=function(){return[{color:"255,255,255",supportLine:!0},{color:"15, 82, 169"},{color:"173, 57, 76"},{color:"48, 220, 155"}]};return iOS9Curve2})());var SiriWave2=((function(){function SiriWave3(_a){var _this=this;var container=_a.container,rest=__rest(_a,["container"]);this.phase=0;this.run=!1;this.curves=[];var csStyle=window.getComputedStyle(container);this.opt=__assign({container,style:"ios",ratio:window.devicePixelRatio||1,speed:0.2,amplitude:1,frequency:6,color:"#fff",cover:!1,width:parseInt(csStyle.width.replace("px",""),10),height:parseInt(csStyle.height.replace("px",""),10),autostart:!0,pixelDepth:0.02,lerpSpeed:0.1,globalCompositeOperation:"lighter"},rest);this.speed=Number(this.opt.speed);this.amplitude=Number(this.opt.amplitude);this.width=Number(this.opt.ratio*this.opt.width);this.height=Number(this.opt.ratio*this.opt.height);this.heightMax=Number(this.height/2)-6;this.color="rgb(".concat(this.hex2rgb(this.opt.color),")");this.interpolation={speed:this.speed,amplitude:this.amplitude};this.canvas=document.createElement("canvas");var ctx=this.canvas.getContext("2d");if(ctx===null){throw new Error("Unable to create 2D Context")}
this.ctx=ctx;this.canvas.width=this.width;this.canvas.height=this.height;if(this.opt.cover===!0){this.canvas.style.width=this.canvas.style.height="100%"}else{this.canvas.style.width="".concat(this.width/this.opt.ratio,"px");this.canvas.style.height="".concat(this.height/this.opt.ratio,"px")}
switch(this.opt.style){case "ios9":this.curves=(this.opt.curveDefinition||iOS9Curve.getDefinition()).map(function(def){return new iOS9Curve(_this,def)});break;case "ios":default:this.curves=(this.opt.curveDefinition||ClassicCurve.getDefinition()).map(function(def){return new ClassicCurve(_this,def)});break}
this.opt.container.appendChild(this.canvas);if(this.opt.autostart){this.start()}}
SiriWave3.prototype.hex2rgb=function(hex){var shorthandRegex=/^#?([a-fd])([a-fd])([a-fd])$/i;hex=hex.replace(shorthandRegex,function(m,r,g,b){return r+r+g+g+b+b});var result=/^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex);return result?"".concat(parseInt(result[1],16).toString(),",").concat(parseInt(result[2],16).toString(),",").concat(parseInt(result[3],16).toString()):null};SiriWave3.prototype.intLerp=function(v0,v1,t){return v0*(1-t)+v1*t};SiriWave3.prototype.lerp=function(propertyStr){var prop=this.interpolation[propertyStr];if(prop!==null){this[propertyStr]=this.intLerp(this[propertyStr],prop,this.opt.lerpSpeed);if(this[propertyStr]-prop===0){this.interpolation[propertyStr]=null}}
return this[propertyStr]};SiriWave3.prototype.clear=function(){this.ctx.globalCompositeOperation="destination-out";this.ctx.fillRect(0,0,this.width,this.height);this.ctx.globalCompositeOperation="source-over"};SiriWave3.prototype.draw=function(){this.curves.forEach(function(curve){return curve.draw()})};SiriWave3.prototype.startDrawCycle=function(){this.clear();this.lerp("amplitude");this.lerp("speed");this.draw();this.phase=(this.phase+Math.PI/2*this.speed)%(2*Math.PI);if(window.requestAnimationFrame){this.animationFrameId=window.requestAnimationFrame(this.startDrawCycle.bind(this))}else{this.timeoutId=setTimeout(this.startDrawCycle.bind(this),20)}};SiriWave3.prototype.start=function(){if(!this.canvas){throw new Error("This instance of SiriWave has been disposed, please create a new instance")}
this.phase=0;if(!this.run){this.run=!0;this.startDrawCycle()}};SiriWave3.prototype.stop=function(){this.phase=0;this.run=!1;if(this.animationFrameId){window.cancelAnimationFrame(this.animationFrameId)}
if(this.timeoutId){clearTimeout(this.timeoutId)}};SiriWave3.prototype.dispose=function(){this.stop();if(this.canvas){this.canvas.remove();this.canvas=null}};SiriWave3.prototype.set=function(propertyStr,value){this.interpolation[propertyStr]=value};SiriWave3.prototype.setSpeed=function(value){this.set("speed",value)};SiriWave3.prototype.setAmplitude=function(value){this.set("amplitude",value)};return SiriWave3})());return SiriWave2}))}});var import_siriwave=__toESM(require_siriwave_umd());var dom=document.createElement("div");var siriWave;siriWave=new import_siriwave.default({container:dom,height:300,style:"ios9",width:250});siriWave.start();this.ondestroy=()=>{siriWave.dispose();console.warn("Removed")};return dom