Running WL in a browser
There is a set of JS and WL libraries, which originally designed for WLJS Notebook project. It comes with a tiny Wolfram Language interpreter written in Javascript. It covers most needs in small numeric calculations, plotting and dynamics. To unlock full potential see dynamics section.
Details
A shortcut
If you have git installed. Simply clone
git clone https://github.com/JerryI/wl-wlx
cd wl-wlx
wolframscript -f Examples/WLJSBasic/WLJSBasic.wls
that will run the simplest example possible
Preparations
The packages used in this tutorial cannot be downloaded using native Wolfram Paclet manager. Please consider to use LPM
Get["https://raw.githubusercontent.com/JerryI/wl-localpackages/main/Kernel/LPM.wl"]
(* or just download this will and add it to your project *)
In order to scope all libraries to your project and provide paths to necessary CSS tables and Javascript files we need to add a couple of libraries
(* LPM Autoinstall and cache *)
Uncompress["1:eJx1T8tuwjAQDBT1wo0/6Ac0vveKKAoNCJoDZ5NugsXGtrwbBf6edawKLlzG+5rxzMfJ/TZvWZbRVKBomkms5wLfBmF1NcR0qN6lL/fbfMC0j4Q18
PM80mkmcDR8TlexK0Ug7SKnAh5f9F0aRoGi8y5wVUp5Zvb0pVTQQ96KTn/qCULtLIPlvHad2kAIt0IN+Imu1uh1fdEtkOq0seoHggVUydForuJgbJsCLgSWTj7r7d/q6gMQGWfT
Lt7KLPp4ZPq3+qz0Iv6Yddcj3gGoDVPG"];
PacletRepositories[{
Github -> "https://github.com/KirillBelovTest/Objects",
Github -> "https://github.com/JerryI/Internal",
Github -> "https://github.com/JerryI/CSocketListener" -> "dev2024",
Github -> "https://github.com/JerryI/TCPServer",
Github -> "https://github.com/JerryI/HTTPHandler",
Github -> "https://github.com/JerryI/wl-wlx",
Github -> "https://github.com/JerryI/wl-wljs-packages"
}]
The last line added is a package manager used by WLJS Notebook (yet another package manager), but comes handy here as well.
Then a few more modifications (code continues)
<<KirillBelov`Objects`
<<KirillBelov`Internal`
<<KirillBelov`CSockets`
<<KirillBelov`TCPServer`
<<KirillBelov`HTTPHandler`
<<KirillBelov`HTTPHandler`Extensions`
<<JerryI`WLJSPM`
WLJS`PM`Repositories[{
Github -> "https://github.com/JerryI/wljs-interpreter" -> "dev",
Github -> "https://github.com/JerryI/wljs-sharedlib-d3" -> "master",
Github -> "https://github.com/JerryI/wljs-graphics-d3" -> "dev"
}]
<<JerryI`WLX`
<<JerryI`WLX`Imported`
<<JerryI`WLX`WLJS`
The first two links highlighted are
- wljs-interpreter an interpreter of WL running on Javascript engine
- wljs-graphics-d3 2D graphics library
There are more libraries available...
- wljs-plotly Alternative 2D plots using Plotly.js
- wljs-inputs a GUI elements like buttons, slides and etc (see in section dynamics)
- wljs-graphics3d-threejs 3D graphics
Those libraries are borrowed from WLJS Notebooks
The last few things concerns about HTTP server settings (code continues)
Print["Staring HTTP server..."];
tcp = TCPServer[];
tcp["CompleteHandler", "HTTP"] = HTTPPacketQ -> HTTPPacketLength;
tcp["MessageHandler", "HTTP"] = HTTPPacketQ -> http;
index := ImportComponent["Components/Index.wlx"];
http = HTTPHandler[];
http["MessageHandler", "File"] = GetFileRequestQ[{"css", "js"}] -> (
ImportFile[#, "Base" -> {"wljs_packages"}] &
)
http["MessageHandler", "Index"] = AssocMatchQ[<|"Method" -> "GET"|>] -> Function[x, index[x]]
(* ::End::*)
SocketListen[CSocketOpen["127.0.0.1:8010"], tcp@# &];
(* import wljs-libs data *)
Map[Function[path,
Block[{System`$RemotePackageDirectory = FileNameJoin[{"wljs_packages", FileNameSplit[path] // First}]},
FileNameJoin[{"wljs_packages", path}] // Get // Quiet;
];
], WLJS`PM`Includes["kernel"] ];
"open http://127.0.0.1:8010" // Print;
While[True, Pause[1];];
The highlighted lines are for resolving URLs pointing to above-mentioned libraries and imports inner Wolfram Language packages.
Page design
Now a boilerplate code as we have seen in the previous sections
Main := ImportComponent["Main.wlx"];
<Main Request={$FirstChild}/>
In principle you don't have to use this intermediate Index.wlx
file and import directly Main.wlx
. However here it forces WLX importer to perform parsing for every request, so that one can observe changes without restarting Wolfram Kernel.
Now our entry-point component
(* /* HTML Page */ *)
ExtensionsJS = (FileNameToURLPath[#]) &/@ WLJS`PM`Includes["js"];
ExtensionsStyles = With[{Path = FileNameToURLPath[#]},
<link rel="stylesheet" href="{Path}"/>
] &/@ WLJS`PM`Includes["styles"] // ToStringRiffle;
App = ImportComponent["App.wlx"];
<html>
<head>
<title>WLX Template</title>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"/>
<WLJSHeader List={ExtensionsJS}/>
<ExtensionsStyles/>
</head>
<body>
<div class="min-h-full">
<header class="bg-white shadow">
<div class="flex items-center mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold tracking-tight text-gray-900">WLJS Basics</h1>
</div>
</header>
<main>
<App/>
</main>
</div>
</body>
</html
Those lines are for embedding necessary Javascript and CSS tables to the head element of our page, which will be imported by server from our wljs libraries.
Now everything is set, we can move on to the main part.
Usage
An access to the interpreter can be done by wrapping Wolfram Expressions via special tag or function
P = Plot[Sin[x], {x, -2Pi, 2Pi}];
<div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
<WLJS><P/></WLJS>
</div>
or
P = Plot[Sin[x], {x, -2Pi, 2Pi}] // WLJS;
<div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
<P/>
</div>
What it does it exports the result of the evaluation to ExpressionJSON
and embeds it to the page, where the tag is located. Lately when the page is loaded, a javascript code will be executed. If there is any graphics output presented it will embed it directly to the <div>
element.
Most 2D plotting functions of Mathematica are covered, as well as 3D, but with some limitations. Please consider the reference section on those libraries.
Styling
One can pass any class names (from CSS table) to <WLJS>
tag to customize the output
<style>
.myClass {
position:absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
<WLJS Class={"myClass"}><P/></WLJS>
Inputs, buttons, UI...?
To use GUI element as etc., please have a look @ dynamics section.
A note on Javascript functions
This is covered by WLJS Interpreter, however the general idea is, that any symbol with own- or down- values is represented as
<script type="module">
core.MyFunction = async (args, env) => {
const data = await interpretate(args[0], env);
element.innerHTML = data;
}
</script>
try to embed the following symbol onto a page
Test = MyFunction["Hello World!"];
<WLJS><Test/></WLJS
or if you use XML attributes
<script type="module">
core.MyFunction = async (args, env) => {
const data = await core._getRules(args, env);
console.log(data);
}
</script>
and
<WLJS><MyFunction Data={"Hello World"}/></WLJS
check the browser's console.
There are much more to cover on this topic. Please consider to read the original docs on WLJS Interpreter.