WLJS LogoWLJS Notebook

Manipulate

Wolfram Kernel
Manipulate[expr_, {u_Symbol, min_, max_}..]
Manipulate[expr_, {{u_Symbol, initial_}, min_, max_}..]
Manipulate[expr_, {{u_Symbol, initial_}, min_, max_, step_}..]
Manipulate[expr_, {{u_Symbol, initial_, label_String}, min_, max_, step_}..]
Manipulate[expr_, {{u_Symbol}, values_List}..]
Manipulate[expr_, {{u_Symbol, initial_}, values_List}..]
Manipulate[expr_, {{u_Symbol, initial_, label_String}, values_List}..]

generates a version of expr with controls added to allow interactive reevaluation.

Manipulate can be used on any Wolfram expression. Graphics or images passed as expr or part of it will be automatically optimized using JIT transpiler if possible. This provides an immediate mode for a user to construct interactive widgets.

By its nature, all interactivity in WLJS Notebooks is implemented in retained mode, while Manipulate is a wrapper over low-level building blocks such as Offload, InputRange, etc., with a diff algorithm used to optimize evaluation of changes. For frequently changing data or complex visuals, we still recommend using these low-level building blocks.

Consider also ManipulatePlot and ManipulateParametricPlot for basic interactive curve plotting.

Examples

Here is a basic example involving symbolics:

Manipulate[Series[Sinc[x], {x, 0, n}], {n, 1, 5, 1}]

Here are a few examples with Plot expressions that effectively use JIT:

Manipulate[
 Plot[Sin[a x + b], {x, 0, 6}], {{a, 2, "Multiplier"}, 1, 
  4}, {{b, 0, "Phase Parameter"}, 0, 10}, ContinuousAction->True]
Manipulate[Plot[1.0 + Sin[w] Sin[x + w],{x,0,5Pi}, Epilog->{
  Red, Point[{8.0, 1.0 + Sin[w] Sin[8.0 + w]}]
}], {w,0,Pi}, ContinuousAction->True]

Another example that solves an ODE on the fly:

Manipulate[
 Plot[Evaluate[
   y[t] /. First[
     NDSolve[ {y''[x] == -x y[x], y[0] == a, y'[0] == b}, 
      y, {x, 0, 4}]]], {t, 0, 4}, 
  Epilog -> {Point[{4, 1/2}], Green, Arrow[{{0, a}, {1, b + a}}], Red,
     Point[{0, a}]},  PlotRange -> 3],
 {{a, 1}, -3, 3},
 {{b, 0}, -3, 3}]

3D plot example:

Manipulate[Plot3D[Sin[n x] Cos[n y], {x,-1,1}, {y,-1,1}], {n, 1, 5, 0.3}, ContinuousAction->True]

Images:

img = ImageResize[ExampleData[ExampleData["TestImage"] // Last], 350];
Manipulate[
  ImageAdjust[img, {c,a}], 
  
  {{c, 0},0,5,0.1}, 
  {{a, 0},0,5,0.1},
  ContinuousAction->True
]

Here is an example with mixed symbolics and graphics:

Manipulate[ Row[{ "m", "==", MatrixForm[m], StreamPlot[Evaluate[m . {x, y}], {x, -1, 1}, {y, -1, 1}, StreamScale -> Large, ImageSize -> Small ] }], {{m, ((*GB[*){{1(*|*),(*|*)0}(*||*),(*||*){0(*|*),(*|*)2}}(*]GB*))}, { ((*GB[*){{1(*|*),(*|*)0}(*||*),(*||*){0(*|*),(*|*)2}}(*]GB*)) -> "Nodal source", ((*GB[*){{1(*|*),(*|*)1}(*||*),(*||*){0(*|*),(*|*)1}}(*]GB*)) -> "Degenerate source", ((*GB[*){{0(*|*),(*|*)1}(*||*),(*||*){-1(*|*),(*|*)1}}(*]GB*)) -> "Spiral source", ((*GB[*){{-1(*|*),(*|*)0}(*||*),(*||*){0(*|*),(*|*)-2}}(*]GB*)) -> "Nodal sink", ((*GB[*){{-1(*|*),(*|*)1}(*||*),(*||*){0(*|*),(*|*)-1}}(*]GB*)) -> "Degenerate sink", ((*GB[*){{0(*|*),(*|*)1}(*||*),(*||*){-1(*|*),(*|*)-1}}(*]GB*)) -> "Spiral sink", ((*GB[*){{0(*|*),(*|*)1}(*||*),(*||*){-1(*|*),(*|*)0}}(*]GB*)) -> "Center", ((*GB[*){{1(*|*),(*|*)0}(*||*),(*||*){0(*|*),(*|*)-2}}(*]GB*)) -> "Saddle"}}]

Options

ContinuousAction

By default, this is False, which means that any update happens after the user's action, not before.

"ControlsLayout"

By default, this is "Vertical". Another possible value is "Horizontal".

PerformanceGoal

By default, this is "Speed", which involves the JIT Transpiler. Change it to any other value to disable it completely.

"JITFeature"

By default, this is True, which involves the JIT Transpiler. If either PerformanceGoal or "JITFeature" is set to a non-default value, the JIT transpiler will be disabled.

Appearance

By default, this is "Default". Set it to None to remove the frame and info boxes.

"UpdateFunction"

Allows to alter the expression, prevent default actions or cause side-effects upon update. The following return values are expected

Function[input,
	(* side effects *)
	(* RETURN *)
	True <- accept change
	False <- prevent default
	_String <- will be written instead
]

One can completely bypass the default reevaluation and use side-effects only

Module[{r},
  Manipulate[Graphics[Disk[{0,0}, r//Offload]],
    {{radius, 1}, 0,1},
    "UpdateFunction" -> Function[value,
      r = value;
      False (* always reject *)
    ]
  ]
]

However, we do recommend to use InputRange directly instead of Manipulate for such cases.

JIT Transpiler

Manipulate, Animate, and Refresh compare successive results and replace supported changing parts with granular Offload updates. This is most effective when every result has the same structure and only numeric arrays or other supported payloads change.

An unsupported change does not normally make the expression invalid: the widget falls back to replacing the complete result. This is a performance fallback, not an evaluation error—the kernel result is still evaluated and displayed. Complete replacement can remain fast for small or textual results and is often entirely reasonable for slowly changing data or interactions that do not need low latency. Treat the recommendations below as performance guidance and optimize when responsiveness actually requires it.

The exception is an option documented as ignored below; such an option is not updated while the optimized expression remains active.

The practical rule

Keep the expression tree stable:

  • Keep the same heads, argument counts, wrapper nesting, graphics options, and number of graphics primitives.
  • Keep lists that describe structure at the same length. In particular, do not add or remove curves, polygon groups, legend entries, or plot components as a control changes.
  • Change data inside a supported primitive rather than conditionally replacing the primitive. Prefer an empty or degenerate data array over inserting and removing the entire primitive.
  • Keep auxiliary primitive arguments and options static. Support for a head does not imply that every possible form of that head can be patched.

For plots, adaptive sampling, exclusions, contours, and meshes can change the number of generated primitives. A fixed PlotRange, Mesh -> None, and, where acceptable, bounded or disabled adaptive refinement such as MaxRecursion -> 0 make the generated structure more predictable.

Supported changing parts

The following are the useful JIT targets implemented by the current diff rules:

Changing partSupported context and constraints
Coordinates/data of LineDirectly inside Graphics or Graphics3D, and inside a 2D or 3D GraphicsComplex
Coordinates/data of PointDirectly inside Graphics or Graphics3D, and inside a 2D GraphicsComplex; changing point indices inside a 3D GraphicsComplex is not supported
Coordinates/data of Triangle and ArrowDirectly inside Graphics or Graphics3D, but not inside GraphicsComplex
Coordinates/data of PolygonDirectly inside 2D Graphics, or inside a 2D or 3D GraphicsComplex; direct changing polygons are not supported in 3D
Disk, Circle, Sphere, Cuboid, Rectangle, and TubeDirect two-argument forms in Graphics or Graphics3D; do not place the changing primitive inside GraphicsComplex
TextDirect two-argument form, or three-argument form with a static third argument; changing text should be a string
Inset positionTwo-argument Inset in 2D Graphics, with the inset object kept unchanged
Standalone RGBColor, Hue, and Opacity directives2D Graphics only, outside GraphicsComplex; keep Style[...], Directive[...], and all 3D styling static
Translate, GeometricTransformation, Scale, and RotateTransformation parameters in the two-argument forms can change; the three-argument form of Rotate is also supported
Image and Raster dataDimensions must remain unchanged; keep Raster placement and other arguments static
PlotLabel2D Graphics only
LineLegend, PointLegend, and SwatchLegendLabel lists can change while the style list, entry count, and remaining arguments stay fixed
TextView, HTMLView, TeXView, and EditorViewThe first payload can change while the remaining arguments stay fixed

Polygons in 2D and 3D

This distinction is important. A changing 2D polygon can remain a direct primitive:

Graphics[Polygon[points[t]]]

A changing 3D polygon should use an indexed representation in GraphicsComplex. Both the vertices and polygon indices may change:

Graphics3D[
  GraphicsComplex[
    vertices[t],
    Polygon[indices[t]]
  ]
]

Avoid Graphics3D[Polygon[vertices[t]]]: direct changing Polygon data is not a 3D JIT target. In 2D, either the direct form above or an indexed GraphicsComplex can be optimized; the latter is useful when vertices or per-vertex colors are shared.

GraphicsComplex constraints

For changing 2D or 3D vertex or index data, keep the number and arrangement of primitive expressions, the GraphicsComplex argument count, and its option order fixed. Vertex arrays and supported nested primitive index arrays can be updated independently. The transpiler recognizes these forms:

GraphicsComplex[vertices, primitives]
GraphicsComplex[vertices, primitives, VertexNormals -> normals]
GraphicsComplex[vertices, primitives, VertexColors -> colors]
GraphicsComplex[vertices, primitives, VertexNormals -> normals, VertexColors -> colors]
GraphicsComplex[vertices, primitives, VertexColors -> colors, VertexNormals -> normals]
GraphicsComplex[vertices, primitives,
  VertexColors -> colors,
  VertexNormals -> normals,
  VertexTextureCoordinates -> textureCoordinates]

Use rectangular numeric arrays. The array contents and supported nested indices may change, but keep the surrounding primitive list and wrappers structurally compatible. Other option combinations or orders are not JIT-transpiled. A 2D GraphicsComplex supports changing Point, Line, and Polygon data; a 3D GraphicsComplex supports changing Line and Polygon data. Changing Triangle, Arrow, shape primitives, or direct color directives inside a complex is not supported. Use VertexColors for changing per-vertex colors.

Changes to avoid

  • Do not change an expression head, its number of arguments, or the length of a structural list. For example, switching between Line[...] and Point[...], or between one and two plot traces, deoptimizes the result.
  • Do not vary Style[...] or Directive[...], including any of their arguments, inside Graphics or Graphics3D; changes in these expressions are not optimized. Other graphics options and style directives must also remain static apart from the narrow standalone 2D color and opacity cases listed above.
  • Do not animate direct colors or opacity in Graphics3D. In 2D, direct RGBColor[...], Hue[...], and Opacity[...] expressions are optimized outside GraphicsComplex; named colors such as Red and Blue work as well because Wolfram Language evaluates them to RGBColor[...] before diffing.
  • Do not change Texture[...] or Image3D[...]. A static texture may remain in the expression, and vertex texture coordinates are supported only in the exact GraphicsComplex form shown above.
  • Do not change image or raster dimensions between updates.
  • Do not rely on changing PlotRange or AxesOrigin: their mutations are deliberately ignored by the diff engine. Set them once, commonly to an explicit range or Full. A changing PlotLabel is supported only for 2D graphics.

If the visual genuinely needs to change topology on most updates, full replacement or a purpose-built retained-mode implementation with Offload is usually a better fit. For interactive or animated curves, also consider ManipulatePlot, ManipulateParametricPlot, AnimatePlot, or AnimateParametricPlot.

When Animate cannot build a JIT path, its refresh rate is limited to 5 FPS. Manipulate and Refresh fall back to complete result updates when a runtime diff cannot be applied.

Debugging

To see the latest message of JIT failure - evaluate the symbol:

CoffeeLiqueur`Extensions`Manipulate`Diff`$lastJITFailure

Portability

Same as for Animate, Manipulate widgets can be shared as MDX or HTML in automatic mode.

Keep the number of possible states determined by all your sliders and selection boxes below 500-600.

MMAView

MMAView wrapper allows to use native Wolfram Engine rendering engine for manipulated expressions. It uses a parallel kernel to rasterize the provided expression and stream updates to the frontend.

Manipulate[Plot3D[Sin[n x] Cos[n y], {x,-1,1}, {y,-1,1}], {n, 1, 5, 1}] // MMAView

It literally streams uncompressed raster images in real-time. Please do not overuse it

Supported output forms

On this page