▲ 33 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 — 10 hours ago
▲ 1 r/wljs

Lorentz Oscillators fitting widget

I needed to fit 10-20 absorption spectrum manually, so I wrote a small program for it using WLJS and Wolfram.

Nothing more than a few nested `Row`, `Column` with `Graphics` inside

Graphics[{
    Point[points//Offload],
    ColorData[97][4], Line[model//Offload],
    ColorData[97][1], EventHandler[Disk[first[[{1,2}]], Offset[{4,4}]], {
      "drag" -> Function[xy,
        first = {xy[[1]], xy[[2]]/L - bg, first[[3]]};
        rebuild;
      ],
      "zoom" -> Function[z,
        first = {first[[1]], first[[2]], z/10};
        rebuild;
      ]
    }],
    ColorData[97][2], EventHandler[Disk[second[[{1,2}]], Offset[{4,4}]], {
      "drag" -> Function[xy,
        second = {xy[[1]], xy[[2]]/L - bg, second[[3]]};
        rebuild;
      ],
      "zoom" -> Function[z,
        second = {second[[1]], second[[2]], z/10};
        rebuild;
      ]
    }]
  }, ImageSize->{1.5 323, 300}, Frame->True, 
     FrameLabel->{"wavenumber (1/cm)", "absorption"},
     "TransitionDuration"->150
  ]
u/Inst2f — 11 hours ago
▲ 4 r/wljs

Sand/Water Automaton

This post/notebook is a small side-view "falling sand" experiment. We start with the simplest material - sand. Each grain is one cell on a grid. A grain should fall if there is empty space below it, come to rest when blocked, and sometimes topple sideways when it is sitting in an unstable little stack. Those three behaviors are enough to make a recognizable pile 🏝️

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

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

Emergent Systems with `Refresh`

Here we recreate a primitive emergent system using nothing more than scoped Refresh and a single external symbol

A single "atom":

Module[{a = 0}, Refresh[
    a = If[a>9, 0, a+1]; 
    Graphics[{
      Circle[{0,0}, 1.0],
      {Opacity[(1 - a/10) // Offload], Yellow, Disk[{0,0}, 0.9]},
      Line[{{0,0}, {Cos[0.628 a], Sin[0.628 a]} // Offload}],
      Directive[FontSize->24], Text[Offload[a], {0,0.2}, {0,0}]
    }, ImageSize->{100,100}], 
0.2]]

Then you add basic interaction via external symbol, i.e.

makeAtom[shared_, i_:0] := Module[{a = i}, 
  Refresh[
    a = Clip[
       Which[
          a > 9, shared = True; 0,
          shared, shared = False; a + Boole[a > 5]*3 - 1,
          True, a + 1
       ],
       {0, 10}
    ];
    Graphics[{
      Circle[{0,0}, 1.0],
      {Opacity[(1 - a/10) // Offload], Yellow, Disk[{0,0}, 0.9]},
      Line[{{0,0}, {Cos[0.628 a], Sin[0.628 a]} // Offload}],
      Directive[FontSize->24], Text[Offload[a], {0,0.2}, {0,0}]
    }, ImageSize->{100,100}], 
  0.2]
]
SetAttributes[makeAtom, HoldFirst];

Make many of them with a random initial phase:

env2 = False;
{makeAtom[env2, 2], makeAtom[env2, 5],  makeAtom[env2, 8]}//Row

and observe emergent patterns!

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

WLJS is out 3.0.8 ⛅️

https://wljs.io/releases/3.0.8

  • Added a new Workers API for asynchronous parallel Wolfram kernels.
  • Improved Shallow with typed expression summarization for large and opaque objects like ByteArray and TimeSeries.
  • Notebook UX improvements, including focused-cell autoscroll.
  • Fixed race conditions during multiple Rasterize calls.
  • Improved MCP server summarization, cell evaluation options, CLI behavior, and error messages.
  • WLJS remains on Wolfram Engine 14.3 for now due to early Wolfram Engine 15.0 performance regressions.
  • Fixed parser, graphics, rounding, and stability issues, including BezierCurve offloading and GraphicsComplex index-buffer handling.
reddit.com
u/Inst2f — 9 days ago

Can SpokenString be used for compressing expressions for LLMs?

I found a golden gem in a standard library `SpokenString`. It might not be token efficient, but it feels more like stream-like form of representing expressions (no need in backtracking to see the full picture)

here an example

> SpokenString[Circle[{0,0}, 1/2]]
< "a circle of radius 1 half centered at 0, 0"

> SpokenString[{a, Table[RandomReal[{-1,1},3], {1000}]}, "ArraySizeLimit"->10]
< "the list a, the list the list 0.151, 0.808, 0.353, skip 998 elements, the list minus 0.999, 0.255, 0.583"

It has extra parameters of depth and max number of arguments, that can act like zoom in/out on expression.

What do you think?

reddit.com
u/Inst2f — 17 days ago
▲ 2 r/wljs

Workers API in Wolfram

Hi there! We are working on a new API for parallel computing in WLJS Notebook. Workers API uses lightweight Wolfram Sub-Kernels communicating using shared memory, which are not limited by the freeware Wolfram Engine license.

Why not ParallelSubmit?
They are conceptually the same, but it is impossible to read out the result without waiting on the main kernel. Workers API is entirely async and uses subscription/events model.

u/Inst2f — 18 days ago
▲ 7 r/wljs+1 crossposts

How fast can a basic sand simulation get without Compile?

ClearAll[blockStep, nextField];

blockStep[field_, p_, phase_] := Module[
  {
    out = field, nr, nc, r0, c0, r1, c1, rows, cols,
    sub, dims, blocks, tl, tr, bl, br,
    fallL, fallR, stackL, stackR, tumble
  },

  {nr, nc} = Dimensions[field];
  {r0, c0} = {1, 1} + phase;

  r1 = r0 + 2 Quotient[nr - r0 + 1, 2] - 1;
  c1 = c0 + 2 Quotient[nc - c0 + 1, 2] - 1;

  rows = r0 ;; r1;
  cols = c0 ;; c1;
  sub = out[[rows, cols]];
  dims = Quotient[Dimensions[sub], 2];

  blocks = Transpose[
    ArrayReshape[sub, {dims[[1]], 2, dims[[2]], 2}],
    {1, 3, 2, 4}
  ];

  tl = blocks[[All, All, 1, 1]];
  tr = blocks[[All, All, 1, 2]];
  bl = blocks[[All, All, 2, 1]];
  br = blocks[[All, All, 2, 2]];

  (* Vertical falling *)
  fallL = (1 - Unitize[tl - 1]) (1 - Unitize[bl]);
  fallR = (1 - Unitize[tr - 1]) (1 - Unitize[br]);

  (* Two stacked grains may spread sideways *)
  stackL =
    (1 - Unitize[tl - 1]) (1 - Unitize[bl - 1]) *
    (1 - Unitize[tr]) (1 - Unitize[br]);

  stackR =
    (1 - Unitize[tr - 1]) (1 - Unitize[br - 1]) *
    (1 - Unitize[tl]) (1 - Unitize[bl]);

  tumble = Unitize[stackL + stackR] *
    UnitStep[p - RandomReal[1, dims]];

  tl = (tl - fallL) (1 - tumble);
  tr = (tr - fallR) (1 - tumble);
  bl = (bl + fallL) (1 - tumble) + tumble;
  br = (br + fallR) (1 - tumble) + tumble;

  blocks[[All, All, 1, 1]] = tl;
  blocks[[All, All, 1, 2]] = tr;
  blocks[[All, All, 2, 1]] = bl;
  blocks[[All, All, 2, 2]] = br;

  out[[rows, cols]] = ArrayReshape[
    Transpose[blocks, {1, 3, 2, 4}],
    Dimensions[sub]
  ];

  out
];

nextField[field_, p_: 0.5] :=
  blockStep[blockStep[field, p, 0], p, 1];
u/Inst2f — 20 days ago
▲ 19 r/wljs+1 crossposts

Markov Algorithms, Mazes, Desert with Sand and Pattern Matching

Let's explore how Markov algorithms and Wolfram Language pattern matching can generate mazes, rivers, falling sand, and other cellular automata through simple rewriting rules.

https://wljs.io/blog/markov

Adding a grain of randomness to a Turing complete language

RB → RR becomes a growth model

and this

RBB → GGR
RnG → GnR

is a maze backtracker!

u/Inst2f — 20 days ago
▲ 3 r/wljs

Building an Interactive Nanographene Constructor

A behind-the-scenes look at porting the Nanographenes Builder application to WLJS Notebook. The app helps researchers design nanographene hydrocarbons, analyze their molecular structures through graph-theoretical models, and experiment with new molecular configurations, while this tutorial explores reactive interfaces, event handling, asynchronous execution, and practical patterns for developing interactive Wolfram Language applications with WLJS.

https://wljs.io/blog/graphene-app

u/Inst2f — 26 days ago
▲ 4 r/wljs+1 crossposts

Animating Images in a Computational Notebook

My attempt to make a nice looking animated video on technical topic using `Graphics` primitives with lots of helper functions 🙂

Heh, I've never tried making YT videos on technical topic with a voiceover. It turned out much more challenging and fun, than I expected.

Sorry for my slavic accent ;)

Video was made entirely in WLJS Notebook
Voiceover in Da Vinci Resolve

youtube.com
u/Inst2f — 30 days ago
▲ 4 r/wljs

WLJS 3.0.7 is out 🍰

This release adds TraditionalForm, improves settings reload behavior, expands the wljs CLI, and includes major internal rendering updates for better stability.

Check this out https://wljs.io/releases/3.0.7

u/Inst2f — 30 days ago
▲ 3 r/wljs

Custom materials for Graphics3D primitives

Use Javascript cells to define your own shader materials:

.js
function vertexShader() {
  return `
    varying vec3 vUv; 

    void main() {
      vUv = position; 

      vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
      gl_Position = projectionMatrix * modelViewPosition; 
    }
  `;
}

function fragmentShader()  {
  return `
      uniform vec3 colorA; 
      uniform vec3 colorB; 
      varying vec3 vUv;

      void main() {
        gl_FragColor = vec4(mix(colorA, colorB, vUv.z), 1.0);
      }
  `;
}

let THREE;
interpretate.shared.THREE.load().then(() => {
  THREE = interpretate.shared.THREE.THREE;
})

core.CustomMaterial = async (args, env) => {
  let uniforms = {
    colorB: {type: 'vec3', value: new THREE.Color(0xACB6E5)},
    colorA: {type: 'vec3', value: new THREE.Color(0x74ebd5)}
  }

  return (function() {
    return new THREE.ShaderMaterial({
      uniforms: uniforms,
      fragmentShader: fragmentShader(),
      vertexShader: vertexShader(),
    });
  })
}

Then use it on your plots like

Graphics3D[{
  Translate[PolyhedronData["Dodecahedron"][[1]]//N , {-2,0,0}],
  MeshMaterial[CustomMaterial[]],
  Translate[PolyhedronData["Dodecahedron"][[1]]//N , {2,0,0}]
}]
u/Inst2f — 1 month ago
▲ 3 r/wljs

Did you now you can use Excalidraw in markdown and slide cells?

Type !![]

u/Inst2f — 1 month ago

Any chance Apple Silicon support (near future)?

Metal framework keeps getting better and M5,6,7 are on the way… This game is too great not to be ported to other platforms

reddit.com
u/Inst2f — 1 month ago

Found in Budapest (prototype of City17, railway station). HL3 confirmed?

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

Do you like flowers?

A small MarkovJunior-style rewrite engine in vanilla (almost) Wolfram Language https://github.com/JerryI/MarkovJunior

Why WL? It is generally built around pattern matching; therefore, MarkovJunior can be implemented easily using native ReplaceAll, Rule, and Pattern symbols. Most of the code is used for building a friendly API and performing error checks.

For example random filling can be done using:

Black -> Red

For self-avoiding walk:

{a___, Red,Black,Black, b___} :> {a, White,Gray,Red, b}

For this particular example (flowers) it is written as a set of replacing rules, which gradually build soil/sky and then grows some flowers...

AppendTo[rules, {
1, 1, Automatic, {
(* seed the soil region *)
Black -> Yellow
}
}];

AppendTo[rules, {
1, 3, Automatic, {
(* seed several sky regions *)
Black -> Red
}
}];

AppendTo[rules, {
1, Infinity, Automatic, {
(* grow the sky and soil regions from their seeds *)
{a___, Red,Black, b___} :> {a, Red,Red, b},
{a___, Yellow,Black, b___} :> {a, Yellow,Yellow, b}
}
}];

AppendTo[rules, {
All, Infinity, Automatic, {
(* convert temporary region colors into sky and soil *)
{a___, Red, b___} :> {a, LightBlue, b},
{a___, Yellow, b___} :> {a, Brown, b}
}
}];

AppendTo[rules, {
1, Infinity, All, {
(* plant the first stem segment along the soil line *)
{
bf___,
{a1___, LightBlue,LightBlue,LightBlue, b1___},
{a2___, LightBlue,LightBlue,LightBlue, b2___},
{a3___, Brown,Brown,Brown, b3___},
af___
} :> {
bf,
{a1 , LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Green,LightBlue, b2 },
{a3 , Brown,Brown,Brown, b3 },
af
} /; Length[{a1}]==Length[{a2}]==Length[{a3}]
}
}];

AppendTo[rules, {
1, Infinity, "MirrorX", {
(* grow stems and leaves with mirrored variants *)
(* weight it with some probabillity as well *)
{
bf___,
{a1___, LightBlue,LightBlue,LightBlue, b1___},
{a2___, LightBlue,LightBlue,Green, b2___},
{a3___, LightBlue,LightBlue,LightBlue, b3___},
af___
} :> {
bf,
{a1 , LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Green,Green, b2 },
{a3 , LightBlue,LightBlue,LightBlue, b3 },
af
} /; Length[{a1}]==Length[{a2}]==Length[{a3}],

{
bf___,
{a1___, LightBlue,LightBlue,LightBlue, b1___},
{a2___, LightBlue,LightBlue,LightBlue, b2___},
{a3___, LightBlue,Green,Green, b3___},
af___
} :> {
bf,
{a1 , LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Green,LightBlue, b2 },
{a3 , LightBlue,Green,Green, b3 },
af
} /; Length[{a1}]==Length[{a2}]==Length[{a3}],

{
bf___,
{a0___, LightBlue,LightBlue,LightBlue, b0___},
{a1___, LightBlue,LightBlue,LightBlue, b1___},
{a2___, LightBlue,LightBlue,LightBlue, b2___},
{a3___, LightBlue,Green,LightBlue, b3___},
af___
} :> {
bf,
{a0 , LightBlue,LightBlue,LightBlue, b0 },
{a1 , LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Green,LightBlue, b2 },
{a3 , LightBlue,Green,LightBlue, b3 },
af
} /; Length[{a1}]==Length[{a2}]==Length[{a3}]==Length[{a0}],

{
bf___,
{a0___, LightBlue,LightBlue,LightBlue,LightBlue, b0___},
{a1___, LightBlue,Green,LightBlue,LightBlue, b1___},
{a2___, LightBlue,Green,LightBlue,LightBlue, b2___},
{a3___, LightBlue,Green,LightBlue,LightBlue, b3___},
af___
} :> {
bf,
{a0 , LightBlue,LightBlue,LightBlue,LightBlue, b0 },
{a1 , LightBlue,LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Green,Green,LightBlue, b2 },
{a3 , LightBlue,Green,LightBlue,LightBlue, b3 },
af
} /; RandomReal[]<0.5 && Length[{a0}]==Length[{a1}]==Length[{a2}]==Length[{a3}],

{
bf___,
{a1___, LightBlue,LightBlue,LightBlue, b1___},
{a2___, LightBlue,LightBlue,LightBlue, b2___},
{a3___, LightBlue,Green,LightBlue, b3___},
af___
} :> {
bf,
{a1 , LightBlue,LightBlue,LightBlue, b1 },
{a2 , LightBlue,Red,LightBlue, b2 },
{a3 , LightBlue,Green,LightBlue, b3 },
af
} /; RandomReal[]<0.2 && Length[{a1}]==Length[{a2}]==Length[{a3}]
}
}];

AppendTo[rules, {
All, Infinity, All, {
(* turn mature stems into blossoms *)
{
bf___,
{a0___, LightBlue,LightBlue,LightBlue, b0___},
{a1___, LightBlue,Red,LightBlue, b1___},
{a2___, LightBlue,Green,LightBlue, b2___},
{a3___, LightBlue,Green,LightBlue, b3___},
af___
} :> {
bf,
{a0 , LightBlue,Red,LightBlue, b0 },
{a1 , Red,Yellow,Red, b1 },
{a2 , LightBlue,Red,LightBlue, b2 },
{a3 , LightBlue,Green,LightBlue, b3 },
af
} /; Length[{a0}]==Length[{a1}]==Length[{a2}]==Length[{a3}]
}
}];

u/Inst2f — 2 months ago
▲ 26 r/u_Inst2f+3 crossposts

Rewrite rules automaton

This idea is solely based on an amazing project MarkovJunior, which is essentially probabilistic pattern matching machine. I though, "ha. This must be the best fit for Wolfram Language!".

Indeed, no tricks were used, except mirroring and rotating the "canvas" to generalize 1D rules into 2D, the rest is purely default pattern matching of the language:

r[{before___, {a___, (RGBColor[1, 0, 0]),(GrayLevel[0]),(GrayLevel[0]), b___}, after___}] := 
  {before, {a, (RGBColor[0, 0, 1]),(RGBColor[0, 0, 1]),(RGBColor[1, 0, 0]), b}, after} /; (RandomReal[]&lt;=0.5);

r[{before___, {a___, (RGBColor[1, 0, 0]),n_,(RGBColor[0, 0, 1]), b___}, after___}] := 
  {before, {a, (RGBColor[0, 0, 1]),n,(RGBColor[1, 0, 0]), b}, after} /; (RandomReal[]&lt;=0.5);

r[any_] := any 

field = Table[(GrayLevel[0]), {30}, {30}];
field[[RandomInteger[{1,30}],RandomInteger[{1,30}]]] = (RGBColor[1, 0, 0]);

Refresh[ArrayPlot[field = applyInAllSymmetries[field]], 0.04]

where

rot[0][m_] := m;
rot[1][m_] := Transpose[Reverse[m]];
rot[2][m_] := Reverse[Reverse /@ m];
rot[3][m_] := Reverse[Transpose[m]];

mirror[m_] := Reverse /@ m;  (* left-right mirror *)

symmetries = Join[
   Table[rot[k], {k, 0, 3}],
   Table[rot[k] @* mirror, {k, 0, 3}]
];

inverseSymmetries = Join[
   Table[rot[Mod[-k, 4]], {k, 0, 3}],
   Table[mirror @* rot[Mod[-k, 4]], {k, 0, 3}]
];

applyInAllSymmetries[m_] :=
  Fold[
    #2[[2]][r[#2[[1]][#1]]] &amp;,
    m,
    Transpose[{symmetries, inverseSymmetries}]
  ];
u/Inst2f — 2 months ago