Did you know WLJS supports export to MDX, HTML and HTML Web Components?
▲ 5 r/wljs

Did you know WLJS supports export to MDX, HTML and HTML Web Components?

The biggest advantage of having a web stack underneath is portability. Our blog posts and docs at wljs.io are literally just exported notebooks.

Besides standalone HTML files, you can export any notebook as web components and publish them anywhere.

One line with a CDN URL lazily loads all the required modules - check it out here: https://github.com/WLJSTeam/web-components. All your graphs (2D or 3D), even Manipulate blocks, stay fully interactive.

Are you a fan of Next.js/React? We support MDX too! Here's our template: https://github.com/WLJSTeam/web-components-mdx

Hope this helps bring high-quality interactive posts to life on your own sites ;)
Our guide: https://wljs.io/frontend/Share/Markdown-React

u/Inst2f — 11 days ago
▲ 2 r/wljs

Quatre éléments (Vera Molnár) Digital Art

Source

a = Line[{{i, j}, {i + 1, j + 1}}];
b = Line[{{i, j + 1}, {i + 1, j}}];
c = Line[{{i + 0.5, j}, {i + 0.5, j + 1}}];
d = Line[{{i, j + 0.5}, {i + 1, j + 0.5}}];

SeedRandom[0];

Framed[
 Graphics[{
   Thickness[0.075],
   Table[RandomChoice[{a, b, c, d}], {i, 25}, {j, 25}]},
  Background -> GrayLevel[0.95]],
 Background -> GrayLevel[0.95]]
u/Inst2f — 13 days ago
▲ 14 r/wljs+1 crossposts

How to tune a guitar

Streaming audio is quite cumbersome in WL. It would be nice to have speakers and a mic as a part of Wolfram Device Framework. Unfortunately, there are only “Camera”, “SerialPort” available.

Jushua and me developed a small library on top of Device Framework and miniaudio.c

https://github.com/JerryI/AudioDriverWL

Now you can simply DeviceOpen[“Microphone”] and have an access to raw numeric array from OS ring buffer or Audio object if you wish. It supports all Wolfram platforms (Mathematica, wolframscript, WLJS). Binaries were prebuilt for Win and Mac only for the moment

Both: sync (poll device in a loop) and async buffer operations (use callback when data arrives) are supported

I tested it with built-in speakers, headphones mic as well as with iRig 2 HD. The latency for streaming (Mic-WL Handler-Speakers) on my Mac is around 60ms or less if you set lower sample rate. Which can still work as a horrible guitar pedal :D

u/Inst2f — 14 days ago
▲ 25 r/ScientificComputing+2 crossposts

How to write apps with Wolfram & WLJS, or swimming against the stream

Any ""serious"" language like C/C++, C#, Pascal/Delphi, and many others offers tons of options for building standalone apps. What about WL/Mathematica/MMA?

One could say, well, yes (and no)! There's the CDF format (which for some reason is officially considered deprecated 👀), webMathematica (costs like a helicopter and is also depreciated), JavaView (a very niche legacy project), and Wolfram Cloud. But I'm personally not a big fan of any kinds of clouds, especially with shady subscription models, and I'd rather keep everything local.

CDF was one of the best solutions so far, but it requires Mathematica (also costly) to build it, which isn't something everyone has, though thankfully, WolframScript/WolframEngine is still freeware.

Other people would argue that WL is meant purely for prototyping, and that you should just "vibe-code something with an LLM and JS" instead. I don't share that point of view, and I've found another way to turn notebooks into tiny or medium-sized apps for my research group. It works so far quite well for a limited number of users: 3-4 - mostly students. I would like to warn you, that it won't work magically for a random Mathematica notebook, otherwise WR would steal this immediately.

If you're interested, here's the full story I wrote:

https://wljs.io/blog/making-apps

PS: The thumbnail image isn't an AI-generated, it's a few UI windows built with this method, printed out and cut from paper 🙂

u/Inst2f — 15 days ago

How to port Mathematica code to WLJS Notebook

You have probably seen a few posts mentioning WLJS. This is an open-source frontend + additional libraries made to bring Mathematica-like experience to a freeware Wolfram Engine and possibly solve some performance/portability issues of Mathematica. Small note: this is not vibecoded product, it stems from a few initial experiments done in 2018 and is not endorsed or affiliated with WRI.

Can any valid Wolfram code or Mathematica expression run well on WLJS Notebook? The answer is yes*, but the devil is in the details.

Static output and anything related to computation is 100% transferable. You can take pretty much any code from Stack Exchange or Wolfram Community and it should work out of the box with a high chance, since we use the same Wolfram Language kernel, in the form of the freeware Wolfram Engine distribution. Manipulate and Animate are compatible too - and fully interactive!

However, low-level dynamics do need to be ported to execute correctly.

Here's a guide on how to port (if actually needed) a program from Wolfram Mathematica to WLJS:

https://wljs.io/frontend/Guides/Migration

Any .nb (Mathematica Notebook) is, in principle, automatically converted with some compromises, of course. We're still improving this! Hope this guide helps you, or your LLM buddy 🙂

reddit.com
u/Inst2f — 21 days ago
▲ 4 r/wljs

Migration Guide: from Mathematica to WLJS

We've been asked many times, in many GitHub issues, whether Mathematica code can run perfectly on a WLJS Notebook. The answer is yes*, but the devil is in the details.

Static output and anything related to computation is 100% transferable. You can take pretty much any code from Stack Exchange or Wolfram Community and it should work out of the box, since we use the same Wolfram Language kernel, in the form of the free Wolfram Engine distribution. Manipulate and Animate are compatible too — and fully interactive!

However, low-level dynamics do need to be ported to execute correctly.

Here's a guide on how to port (if actually needed) a program from Wolfram Mathematica to WLJS:

https://wljs.io/frontend/Guides/Migration

Any .nb (Mathematica Notebook) is, in principle, automatically converted with some compromises, of course. We're still improving this! Hope this guide helps you, or your LLM buddy 🙂

u/Inst2f — 21 days ago
▲ 5 r/wljs

Constrain the coordinates of a point to lie on a circle

Here is a full code

Module[{p = {0, 1}}, 
 Graphics[{ColorData[97][1], Dashed, Circle[], PointSize[0.1], 
   EventHandler[Point[p//Offload], {
     "dragsignal" -> ((p = Normalize[#])&)
   }]}, ImageSize -> Tiny, 
  PlotRange -> 1.5]]
u/Inst2f — 21 days ago
▲ 2 r/wljs

Simple widget with bouncing balls

There is nothing special, but I personally love this little snippet. It reveals the basics of continuous animation together with event bindings in WLJS

Framed@Module[{contents = {}, pts = {}}, 
  EventHandler[
   Graphics[{
     PointSize[0.1], 
     Point[pts // Offload],
     EventHandler[AnimationFrameListener[pts//Offload], 
      Function[Null,
       contents = 
        Map[If[#[[1, 2]] >= 
            0, {#[[1]] - #[[2]], #[[2]] + {0, 0.001}}, {{#[[1, 1]], 
             0}, {1, -0.8} #[[2]]}] &, contents];
       pts = Map[First, contents];
      ]]},
     PlotRange -> {{0, 1}, {0, 1}}], {
      "mousemove" -> Function[xy,
       AppendTo[contents, {xy, {0,0}}]
     ]}
]]
u/Inst2f — 21 days ago
▲ 3 r/wljs

Symbolic notes, scales and chords in Wolfram Language

In this tutorial we start with the low-level Sound and SoundNote expressions used to describe playable audio, then move to the higher-level Music* framework introduced in WL 15 (Wolfram Engine 15). Stay in tune!

https://wljs.io/blog/music-1

u/Inst2f — 22 days ago
▲ 2 r/wljs

WLJS 3.1.0 is ready 🕹️

Get yours here: https://wljs.io/releases/3.1.0

In a nutshell: more stable, better support for new symbols including, symbolic arrays, vectors, non-commutative algebra, incremental objects, standard colors and many more! Apart from that:

  • Added RPM binaries
  • Added rich LaTeX and mixed-content support for plot legends and frame labels.
  • Enhanced Sound with piano-roll previews, an improved synthesizer pipeline, and many new instruments.
  • Added interactive previews for WL 15 music objects, including notes, pitches, chords, scales, and scores.
  • Improved UI and formatting
  • Better autocomplete for Greek letters (use ESC key)
  • Improved ManipulateAnimate functions

https://preview.redd.it/ptlfd108k7gh1.png?width=728&format=png&auto=webp&s=3ab34c568a21b89263f9ae845e7f3799a3632792

https://preview.redd.it/zcbsrmeck7gh1.png?width=920&format=png&auto=webp&s=1f1e293368334258be6e97cfaed6ebc96ee8702f

https://preview.redd.it/ju26aoeck7gh1.png?width=1014&format=png&auto=webp&s=a6c4154a030c1e78aea9a3ab26539a8198c8e8e1

https://preview.redd.it/pjus6oeck7gh1.png?width=706&format=png&auto=webp&s=216a9a037239f6c0449c5a66a3643b6dcee1982d

reddit.com
u/Inst2f — 22 days ago
▲ 2 r/u_Inst2f+1 crossposts

Automatic data analysis, narrative, and 3D sketch from a single prompt using MCP

We've spent a lot of time polishing and optimizing our MCP server so AI agents can work directly with notebook resources. In general, it gives the model a standard set of tools for reading/writing code cells, along with summarization of Wolfram Language expressions (a sort of RAG system), evaluation-kernel access, extensive guardrails, and documentation search (both local and online).

See here: https://wljs.io/frontend/Guides/Command-palette#mcp-server

In this example, we gave it the task of analyzing and nicely presenting a freshly measured absorption spectrum of the spin-chain compound Na₂MnF₅. The original prompt was:

>Use wljs-mcp tools. There is a notebook in the folder with absorption spectra measured for Na2MnF5 at 10K up to 7T. This is a spin-chain compound, we applied B along the a-axis of spin chain. And Ew || a - the same axis. Please make plots with all spectra stacked and separated vertically. Add narrative cells to make it look like a report, make a sketch using Graphics/3D of a compound. The main focus on on two features at around 20-40 cm^-1 which split symmetrically in the field (they are also split in 0 field due to low symmetry of the compound). Thanks

u/Inst2f — 1 month ago
▲ 4 r/wljs

We Used GPT-Luna to Check 5,000 Examples Across Our Documentation Pages

As you might know, we host independent documentation on the Wolfram Language standard library, including tutorials on the language itself. Note that these are not direct ripoffs of WR's material. Most of the pages were carefully crafted by us, whereas for other areas, where we did not have good expertise, we relied on ::usage messages and examples from SE.

For example:

https://wljs.io/frontend/Image/Image (handwritten)

https://wljs.io/frontend/Formatting/AccountingForm (procedurally generated & post-processed)

Nowadays, with the help of AI, we've managed to check many of them against real computational kernels. The whole process took us 3 days and 16 subagents (Claude Haiku) running continuously with persistent memory using markdown files, followed by another sweep today with GPT Luna to recheck everything again. Wow, what a time to live in...

u/Inst2f — 1 month ago
▲ 51 r/wljs+1 crossposts

Yet another stupid experiment with VertexColors and Camera

Just in case, here is a code of this... whatever you call it:

DeviceClose[cam];
cam = DeviceOpen["Camera"];
cam["FrameRate"] = 60;


reference = ImageData[
    ImageResize[ColorConvert[DeviceRead[cam], "GrayScale"], 100],
    "Real32"
];

{height, width} = Dimensions[reference];
indices = Flatten[
  Table[
    With[{a = (row - 1) width + column, b = (row - 1) width + column + 1,
      c = row width + column, d = row width + column + 1},
      {{a, b, c}, {c, b, d}}
    ],
    {row, height - 1}, {column, width - 1}
  ],
  2
];
  
Refresh[Module[{img, length, raw},
  raw = ImageResize[DeviceRead[cam], 100];
  img = ImageData[
    ColorConvert[raw, "GrayScale"],
    "Real32"
  ];
  cols = Flatten[ImageData[
    ColorConvert[raw, "RGB"],
    "Real32"
  ], 1];
  
  length = Length[img] Length[img[[1]]];
  vertices = -MapIndexed[Join[#2, {40.0 #1}]&, img, {2}];
  vertices = Flatten[vertices, 1];

  Graphics3D[GraphicsComplex[vertices, {
    LightBlue, Directive["Roughness"->0.0],
    Polygon[indices]
  }, VertexColors->cols], ViewProjection->"Perspective", ImageSize->Medium]
], 1/60.0]
u/Inst2f — 1 month ago
▲ 5 r/wljs

Playing with surface normals

This is my little experiment for the future rigid-body physics project. Here is a surface from the example:

surface = (*VB[*)(Uncompress["1:eJxTTMoPSuNnYGAoZgESPpnFJWlMyLyiC7sOivxcI3ygaEFps+YnT1YHNHn24Ceb8wwEDxQBVUn7NGLIr5P2mXh+F+eBooSummu86wQPoMmDjZdmOVBk4Gr3mi0aQx5kuqsd84GiDSB69Tt7NPk3bCAHMEDNb/uCLt8NFF124vt+XPIeQNcdFHm5v8jhNdCky5/2o8kXAH0daX7DvigA5JJUDHmI/7/aF3GAGA7s6O6fADL+J4sDDvlMZiADV9iEgD0v5FC07ET63YRJuOVBQXyQhR097MFRMlEYIQ8APFOk1Q=="])(*,*)(*"1:eJxTTMoPSmNmYGAo5gUSYZmp5S6pyflFiSX5RcEcQBHP5Py8zKrUlMwARgaGNCaQQhYgEVSakxrMCmT4JCal5gRzAlk5+Yl5STmpeSkASLwUcA=="*)(*]VB*);

And a code for this little widget:

rayIntersect[a_, {u_,v_}, surface_] := MapThread[Module[{x = {#1, #2}[[All,1]], y = {#1, #2}[[All,2]]},
 <|
   "t"-> (*FB[*)((((*TB[*)Indexed[(*|*)x(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)) (-v+(*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+((*TB[*)Indexed[(*|*)x(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)) (v-(*TB[*)Indexed[(*|*)y(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+u (-((*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(*TB[*)Indexed[(*|*)y(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)))(*,*)/(*,*)(Cos[a] ((*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)-(*TB[*)Indexed[(*|*)y(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(-((*TB[*)Indexed[(*|*)x(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(*TB[*)Indexed[(*|*)x(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)) Sin[a]))(*]FB*), 
   "T"-> (*FB[*)((-v Cos[a]+Cos[a] ((*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(u-(*TB[*)Indexed[(*|*)x(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)) Sin[a])(*,*)/(*,*)(Cos[a] ((*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)-(*TB[*)Indexed[(*|*)y(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(-((*TB[*)Indexed[(*|*)x(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(*TB[*)Indexed[(*|*)x(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)) Sin[a]))(*]FB*),
   "N"-> -Normalize[{-((*TB[*)Indexed[(*|*)y(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*))+(*TB[*)Indexed[(*|*)y(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*),(*TB[*)Indexed[(*|*)x(*|*), {(*|*)1(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)-(*TB[*)Indexed[(*|*)x(*|*), {(*|*)2(*|*)}](*|*)(*1:eJxTTMoPSmNkYGAo5gUSYZmp5S6pyflFiSX5RcHsQBHPvJTUitQUAL2qCoU=*)(*]TB*)}]
  |>
]&, {Drop[surface,-1], Drop[surface,1]}];

selectedRays[args__] := Quiet@Select[rayIntersect[args], TrueQ[(#T >= 0.0 && #T <= 1.0)]&]

Module[{pts = {}, rays = {}, norms = {}, lines = {}},
  ListLinePlot[surface, PlotRange->{-5,5}, Filling->-5, Epilog->{
    LightBlue, Directive["TransitionType"->None],
    Line[lines//Offload],
    Orange, Line[norms//Offload],
    Red, Point[pts//Offload], Black,
    EventHandler[Locator[{0,0}], {"drag" -> Function[xy,
      {pts, norms, lines} = Transpose@Flatten[Table[
        With[{intersection = #t {Cos[a], Sin[a]} + xy}, 
          {
            intersection, 
            {intersection, intersection + 0.4 #N}, 
            {xy, intersection}
          } 
        ]&/@ selectedRays[a, xy, surface],
        {a, 0., 3.14, 0.5}
      ], 1];
    ]}]
  }]
]
u/Inst2f — 1 month ago
▲ 2 r/wljs

Did you know WLJS support Canvas API?

We provide a bridge to Canvas WebAPI, which is a great tool if you need to work with complex raster graphics in retained mode

https://wljs.io/frontend/Advanced/Canvas-2D/Overview

The data is send in a binary form as opt codes for a HW accelerated WebAPI.
Here is a code for an example above:

Needs["Canvas2D`"->"ctx`"];

ClearAll[handler]
SetAttributes[handler, HoldRest]

handler[c_, tcursor_, {width_, height_}] := Module[{
  w = width, h = height, 
  n = 101,    
  cursor = tcursor,
  rotSpeed = 0.02,
  particles,
  theta, rad, col, old
},
  
  (* center point *)
  cx = w/2;
  cy = h/2;
  
  (* semi-transparent drawing *)
  ctx`SetGlobalAlpha[c, 0.5];
  
  (* initialize particles: random angle, radius, colour *)
 particles = Table[
   {
    RandomReal[{0, 2 π}], 
    RandomReal[{0, 150}], 
    ctx`ColorToString[RandomColor[]],
    cursor
   },
   {n}
 ];

  Function[Null,
      cursor = (tcursor);
      
      ctx`SetFillStyle[c, {Black, Opacity[0.05]}];
      ctx`FillRect[c, {0, 0}, {w, h}];
      
      (* draw each particle: from last cursor pos to new one *)
      Do[
        particles[[i, 1]] += rotSpeed;
        {theta, rad, col, old} = particles[[i]];
        Module[{newPos},
          newPos = cursor + {Cos[theta], Sin[theta]}*rad;
          ctx`BeginPath[c];
          ctx`SetLineWidth[c, 4];
          ctx`SetStrokeStyle[c, col];
          ctx`MoveTo[c, old];
          ctx`LineTo[c, newPos];
          ctx`Stroke[c];

          particles[[i,4]] = newPos; 
        ],
        {i, n}
      ];
      ctx`Dispatch[c];
  ]
]

aContext = ctx`Canvas2D[];
tcursor = {250,250};

EventHandler[
    Image[aContext, ImageResolution->{500,500}, Epilog->{
      EventHandler[AnimationFrameListener[aContext], handler[aContext, tcursor, {500,500}]]
    }], {
      "mousemove" -> Function[xy, 
        tcursor = {xy[[1]], xy[[2]]}
      ]
  }
]
u/Inst2f — 1 month ago
▲ 132 r/wljs+2 crossposts

Camera streaming experiment

If you think WL is slow, you never used it with WLJS sauce 🍝

  1. connect to a web camera using device API

    cam = DeviceOpen["Camera"]; cam["FrameRate"] = 60;

  2. resize and pipe the raw image data to 3D point cloud with minor transformations

    Refresh[Module[{img, length}, img = ImageData[ ImageResize[ColorConvert[DeviceRead[cam], "GrayScale"], 100], "Real32" ]; length = Length[img] Length[img[[1]]]; vertices = MapIndexed[Join[#2, {50.0 #1}]&, img, {2}]; vertices = NumericArray[Flatten[vertices, 1], "Real32"];

    Graphics3D[GraphicsComplex[vertices, { Red, PointSize[0.0003], Point[Range[length]] }], ViewProjection->"Perspective", ImageSize->1000] ], 1/45.0]

NumericArray is important here. This forces WL to use more efficient data structure.

u/Inst2f — 2 months ago