Back to Releases

Release notes *3.1.0*

July 25, 2026
docker run -it \
  -v ~/wljs:"/home/wljs/WLJS Notebooks" \
  -v ~/wljs/Licensing:/home/wljs/.WolframEngine/Licensing \
  -v ~/wljs/tmp:/tmp \
  -p 8000:3000 \
  --name wljs \
  ghcr.io/wljsteam/wljs-notebook:main
brew install --cask wljs-notebook

Better compatibility with WL 14.0–15.0

We significantly improved symbol rendering, added various styling options for new plotting functions, and improved the output forms of new symbols.

OpenSUSE and others

We added .rpm binaries!

Exception objects

For example, an Exception object now shows relevant information:

Exception["DataConsistencyError"]
Exception[(*VB[*) {"DataConsistencyError"}, <|"ExceptionValidated" -> True, "ExceptionAPIVersion" -> "1"|> (*,*)(*"1:eJyVUl1v2jAUpVsrbevDpL1U2sPEpOWRCQxpw95sSPox1iEMjPBU41yiiMSmTiJC/27/SK+hH7xOsu71Pef46tjX3xd6tHxfq9XyrxiYrgJtsjtqjFAxRLzMMmG2CC+PrOYzhsBoVfgqmiamKEW6J74cEH4FsizEIgX+A+GO9JoRnLcazQviNTrgkobXulg0CLhuE4hsI7d8Z5scYxgkebFv+VrtuLMDd8+urgvIpgls+ClyfiVhXSRa/arvD3yyYJQU2ljNW8srnUb76iOGyWjQB6kj4I9YOcRzCGXMcRlmh3R3AOmLQvS0ytEMKLn1jdEG4Rcehb0DOW5Iy2lTuKnG4z96yLPbJLyk2o0lp5OZO5+v1vdcOYQx/m+OaaYCEW7i3rgT+9OKZ/PhPSrkzB2Fq7Q7eaDXE5mbIQ+vNBte9ri65XFX0d8633rdAQuTIFiLFV23miGPnXbfLuvuwJHbZ883envpUYkD+mA3IKK/Kt3u0LEp4X/f/xtyVNVLtVJ6o+rwMoq6lrI0BqKfvGZ7nNhPItIcngD41qmR"*)(*]VB*)]

Throw and catch exceptions:

CatchExceptions[
  1+ThrowException[Exception["Null"]],
  All
]
Failure[(*VB[*) "Null", <|"ExceptionAPIVersion" -> "1", "ExceptionTagList" -> {"Null"}|> (*,*)(*"1:eJyVUctOwkAUxVd8bEzcmLjSxC5J6ECluOtACygi6bRIWVnaS9PQzsC0DeCH+j3OAApbk8l9nHNyc+behwmzpyelUim7EwGzlcV4+mlw7tMIQlKkqc/XAp4eSc21CBZnNDdpOIx5XvjJlrg5IMwVBEXuTxIgjwKuBXolhCe1XKkjvVwDDZV1tT4pI9C0CqCgKrjpsRxyKkIvzvLtyL9uw90euNu56uaQDmNYknPBmZwz/ny/FV9JIIxzxiW/H9dhSbjtLkVw7V4LAhYC+RadgnQFGRgrGhZZQY0NgNpAgceBqH4hwTUPFKJAqlI14GXlOG9sQNJ+7LUNpkUBMdyRNh7P5gtCFYQx+RiLNKKW7y2jplOLzOGKpOPBQiiCkWZ7s6ThfhldN8j4gHgdhgftJqF9EjWo8cqytd7oYS+2rLk/M+ZqxSORUm3JJ90dONJaePeJ/WLtQtzjQhbgh+80WW9Qhxfw33VLZb9IElKSyjN5eT/J4AdtHZiQ"*)(*]VB*)]

Symbolic array, matrix, and vector representations

...fill in gaps

ArraySymbol["a", {2,3}]
(*VB[*)(ArraySymbol["a",{2,3}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4vkAjLTC13SU3OL0osyS8KZgKJOAaDZJUSldKYYHp8MotLgkEmGIFJYwCxxQ9A"*)(*]VB*)
MatrixSymbol["a", {2,3}]
(*VB[*)(MatrixSymbol["a",{2,3}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWlMIB4vkAjLTC13SU3OL0osyS8KBgmH+QYzAymlRCUATnQLww=="*)(*]VB*)

and symbolic vectors:

VectorSymbol["a", 45]
(*VB[*)(VectorSymbol["a",45])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4vkAjLTC13SU3OL0osyS8KZgKJBAeDZJUSlcBcE1MApEMMiA=="*)(*]VB*)

Symbolic tensors respect +, -, and other operations, whereas WL would usually treat them as scalars.

a + {1,2,3}
(*VB[*)(VectorSymbol["a",45])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4vkAjLTC13SU3OL0osyS8KZgKJBAeDZJUSlcBcE1MApEMMiA=="*)(*]VB*) + {1,2,3}
{1+a,2+a,3+a}
{1,2,3}+(*VB[*)(VectorSymbol["a",45])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4vkAjLTC13SU3OL0osyS8KZgKJBAeDZJUSlcBcE1MApEMMiA=="*)(*]VB*)

Non-commutative algebra representations

We added support for non-commutative algebra symbols. For example:

alg = CliffordAlgebra[{{x}, {y}, {z}}]
NonCommutativeAlgebra[(*VB[*) <|"Multiplication" -> NonCommutativeMultiply, "Addition" -> Plus, "Unity" -> 1, "Zero" -> 0, "CommutativeVariables" -> {}, "ScalarVariables" -> Automatic, "Generators" -> {{x, y, z}}, "Relations" -> Automatic, "CommutationRelations" -> Automatic, "Structure" -> {"Clifford", {{x}, {y}, {z}}}, "VariableOrder" -> "Increasing", "WordOrder" -> "Lexicographic"|> (*,*)(*"1:eJylkc9LwzAUx+svUC+KF8GTovVW2Lp1bN5a16EwEVrd2TTvVQJpImmC23/vixXsQRAUwpd8v+/l5RNyUemi3gmCoD0jyfR6oU3zkhrD1CtC6ZqGmQ3F9ZbvOSJZGK1srmAljHVMdoWTXiFfI3eWVRLLK4pxPKn5DDCajkbDaAxDFs0G0ziCGCuI62TCB1W97YfskixFazt32uP54ri32KwEvpfHVHtw0oo3KTizQqub8+7UIUkOwmrjGzs4P/dOS+jcAclzsZwj14DlNbkwuQ2TzF/EZBom8zBO/eqlGaXfjIWjp+37DTJ4VHLzmT4Zh7+Te74UCPAfzJc/Mmd/AW73/Mcx2eIH7pWE7Q=="*)(*]VB*)]
NonCommutativeExpand[(x + 2 y + 3 z) ** (4 x + 5 y + 6 z), alg]
-6-3 x**y-6 x**z-3 y**z

Incremental objects

A new feature in recent versions of WL is support for incremental functions and objects, similar to JavaScript function generators and the yield keyword.

obj = IncrementalObject["Identity"][{1, 2}]
IncrementalObject[(*VB[*) "Identity", {"Source" :> {1, 2}, "Position" :> 1} (*,*)(*"1:eJydkM1LxDAQxesXuAoKggieFPTYg9lU1JuLLbuwKLTas2kylUCaLPnA9r93Yhfcm+LlMe+9Yfgxl40p250kSdw5ysz0hbHd+6O1TH+AqELXMTtg3G7FnWOUwhrtcy1qaX1gaixONoq8Bx48axRUVxjfE6BTLrI0YxRSetvw9I4Kkk5vMk4y2hIgvN2OR3ZRltL50Z1t8Kw5Fh66WsJntYfdM+vgYVw9QMmF9MbGdiSKx+ZGidFNUN7K5RNwI6A6QndNyEKA9tIPOP4AlAG59+MATLxoNXynrzbA71iH2NVMBbjww+q/cKdrOA3W5/3KgnPS6D8yuviZgikHX+ZoeHE="*)(*]VB*)]

Return values from the incremental object:

NextValue[obj]
NextValue[obj]
NextValue[obj]
1
2
Failure[(*VB[*) "IncrementalObjectTerminated", <||> (*,*)(*"1:eJyVUlFv2jAQZuumtXuZ1JdJ60srNY9IYMga+hYDoWy0RThQwtOMc0QpiQ2Oo0J/aH/PztC1vE6yvrv7vtPp89kXczVaHFUqleIHAlWbQOn8j681lwnErMxzrrdILz7Ynm8IgVbSdGU8SbUpebYXTg+E7gZEafg8A3aJdFN4tRh+1qu1K+JVm+CSqle/mlcJuG4NiGigtvhoh3xCGKSF2Y98q3ba9wN3r676BvJJCk/sC2pdrZW+Pt83f7VEnBqlrf4+7kZl8b46QRiPBh0QKgb2gpVDPIf4lDouxeiQ1o4gPZCgU4HZPwq19kEHJqTuNHz4tQnDWzVk+V0a9XzlJoL546k7my1XayYdQil7mGGYyoBHT0k7bCbdyYbls+EaO8TUHUXLrDV+9vtjUeghi24UHfbaTN6xpCX936rYeq0BjdIgWPGlv6rXIpY4jY491t2BI7dDXy/xvthRie9xbBPg8b3Mtjs21CX877rPUOtLoSEHaXh2P38EYULQeSq5wT9TsQM+2w/BswL+AuC/oeI="*)(*]VB*)]

Standard colors

WL's new color palette is now available in autocomplete.

StandardBlue
StandardRed 
(*VB[*)(RGBColor[0.4, 0.6, 1])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qmjUTBG7aFxmDwWP7IgYw+GAPAFRyFkk="*)(*]VB*)
(*VB[*)(RGBColor[0.93, 0.27, 0.27])(*,*)(*"1:eJxTTMoPSmNiYGAo5gUSYZmp5S6pyflFiSX5RcEsQBHn4PCQNGaQPAeQCHJ3cs7PyS8qOvxVI6b/0Fv7Io+HVSLr3C8iGAB5sRpI"*)(*]VB*)

Improved formatting of PlotLegends and FrameLabels

Due to a Unicode decoding issue, it wasn't previously possible to embed LaTeX in plot-axis labels or legends, or to mix it with regular labels.

For example, suppose you want to place a LaTeX equation as a plot legend:

Plot[x x, {x,0,1}, PlotLegends->Placed[{
  TeXView["x^2"]
}, {0.2,0.6}]]
(*VB[*)(Legended[ToExpression[FrontEndRef["e8652386-8ccc-4fba-a517-7dcf29fb5173"], InputForm], Placed[LineLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]]}, {TeXView[Offload[URLDecode["x%5E2"]]]}, LegendMarkers -> None, LabelStyle -> {}, LegendLayout -> "Column"], {0.2, 0.6}, Identity]])(*,*)(*"1:eJxdUUtOwzAUDNDyEyzgAEhIsMyCln5YRUADQgoUmoLYOs4zWBgb2Q40B+AQcJJegQ2XYIXYcAT8Ka3atxhZ43nP88bbmeiRhSAI1LqBGwovHcBCIi1kumSYBO6A5zUvWTUQ59TcWSGZs9ymgRMpuI55Hg8AFxplDNIdQ0O72ajV282wjTEO90mGQtTYa4WtHJPaAcnMuU7m7ZCKgV5h2pbtAVDe5ax0bF8WMKOxLlJggN1LqmoNIKbAe1w0cMkQhpxU/z0nlINfxHuuOE5p37FioEOlmUefwQvs5t0nhKkuZeDqN/JiZ/D06FgwIeVw6/XnavgZybqrr0i+v9n6jvyYDQOHmRKs0NC/p/iBg1LUbjPjY/xoH24n0ToXhDCBRr6t0+teYn8oh9SuN9htxLXpfNxH+m3PkXwAqdzVheAzQfpoUAYs1SUDEkxZmpaujWcmqBSFTm3MJoTikU+Utm+UwMckExfZWQ5cmzT/AGE8mx4="*)(*]VB*)

Here we used TeXView directly with TeX code inside. Sometimes, if the expression is too large, you may set the size and anchor manually to better position your legend item:

Plot[x x, {x,0,1}, PlotLegends->Placed[{
  TeXView["x^2~\\text{function}", ImageSize->{150,30}, AlignmentPoint->{Left,Center}]
}, {0.2,0.6}]]
(*VB[*)(Legended[ToExpression[FrontEndRef["c0121a30-70b4-4e2a-b6ae-a865023f7b39"], InputForm], Placed[LineLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]]}, {TeXView[Offload[URLDecode["x%5E2~%5Ctext%7Bfunction%7D"]], ImageSize -> {150, 30}, AlignmentPoint -> {Left, Center}]}, LegendMarkers -> None, LabelStyle -> {}, LegendLayout -> "Column"], {0.2, 0.6}, Identity]])(*,*)(*"1:eJxtUslOwzAQDfsiQIIrUiUkeqxUUkrhVNEFhFS2piCuTjIuFq6NHAdaDtz4BST4En6BCz/BCXHhExjbbKnw4ckZv5l5My8roWzTMc/zknmEUwbXDYikIlqqYAojLeiCiH1HmUVoxgzfDJGOmNgSwo6SQjdF3OxDlGoScghWMRwV1/w1UioWKsVwvbAOPimEGwQKZHOjXPRLtBKWtuioKTKO0E4xbdpcgMSHgg9stKNSGOIYFQFwiGynZMIIIDwBp3ES4YiTCGI68a25xQS4QZzmcRtLtMuYQWgwhfXYFTiCmfzwkkRMD5Rnz0fVka3A3VpdcqnUU+7u/fjppapK9rxW1eODOW9VV2YRYTtMJE81dM5ZdCEgSZiZ5j8dpmkHzn5Xa1VQyiX50m2UnrRbxqEYgmX86ufLTf82X65r6Ot8pUZTgWNIka80sluzuXs90oWA3fzZqGnP7vHCckZWNmfB6OesK3og9JFkQmcT3QWotluvIwnUUIn5n39on6gLUIl9OpBiyFXnEwmBB3rAgXqZ/WSpcz81W2QgUx3Y7rjlnsjq+7Lj+dcg699ejErR2k+Yb7yc"*)(*]VB*)

Use Row to combine LaTeX expressions with normal text labels.

Plot[x x, {x,0,1}, PlotLegends->Placed[{
  Row[{
    TeXView["x^2"],
    "function"  
  }]
}, {0.2,0.6}]]
(*VB[*)(Legended[ToExpression[FrontEndRef["624aae8d-f789-4f65-8128-44fccff58163"], InputForm], Placed[LineLegend[{Directive[Opacity[1.], RGBColor[0.24, 0.6, 0.8], AbsoluteThickness[2]]}, {Row[{TeXView[Offload[URLDecode["x%5E2"]]], "function"}]}, LegendMarkers -> None, LabelStyle -> {}, LegendLayout -> "Column"], {0.2, 0.6}, Identity]])(*,*)(*"1:eJxdUUtOwzAUDNDyEyzgAEhIsMyiaVrCKgIaEFKg0BTE1kmeIaqxkePQ5gAcAk7SK7DhEqwQG46AP4Uo9WIUvzd+mZm3G7MBXrIsK9+UcJvBuAcJ40gwHq3ISgj3QFPHUNYlBGkme4qIF1RtW8IpZ1QENA0mkBQCxQSiPVnuOi5C4KU2PvAObRd3O7bXcjzbdXGSYNzxWt02XlRDGhIGhXy2qj4ApX1KSl0d8gLmOEpFBAQS/ae8qQQgkoPRuCzhiqAEUtz80xxmFIwRo7mha7kwL9Yk9DIu52XPYAjKef8JJZkouaXPj2/IWuDZ8QkjjPPpzsv39fTD5219Pn3+9qrOl2/GbEk4inNGCgHDhywZUcjzTLmZ06Fvav6AjSu3VUvpGcJdlboWiDFhaGZJmbgZhGp5KUTK+WS/Ezg6UFxQ6Y3Reo564SaVC8RHwHPdumR0LnATIYqBRKIkgK2avjp1439miEpWiEitQ4ZVPNK6r1lS71V2OtrzFKiQqf8C4tGjHQ=="*)(*]VB*)

The same can be done with the FrameLabel of your plot; you can put TeXView, or, in general, any other expression, alongside normal text:

Plot[x x, {x,0,1}, FrameLabel->
  {None, Row[{
    TeXView["x^2"],
    "function"  
  }, Spacings->1]},
  Frame->True 
]
(*VB[*)(FrontEndRef["29a39c5a-c0c3-4217-8c1b-80738bc19f00"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG1kmGlsmmybqJhskG+uaGBma61okGybpWhiYG1skJRtaphkYAAB+ohU4"*)(*]VB*)

For example, with a graphic:

Plot[x x, {x,0,1}, FrameLabel->
  {None, Row[{
    Graphics[{StandardRed, Line[{{-1,-1}, {1,1}}]}, ImageSize->10],
    "function"  
  }]},
  Frame->True 
]
(*VB[*)(FrontEndRef["92c965d7-b00e-40e7-b004-ef923bd81869"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKWxolW5qZppjrJhkYpOqaGKSCWSa6qWmWRsZJKRaGFmaWAH7tFUw="*)(*]VB*)

Improved Sound support

We have a nice piano-roll preview of a given sequence of notes!

Sound[{{SoundNote[{"A4", "C4"}]}, SoundNote["E4"], SoundNote["G4"], SoundNote["C5"]}]
(*VB[*)(Sound[{{SoundNote[{"A4", "C4"}]}, SoundNote["E4"], SoundNote["G4"], SoundNote["C5"]}])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5AIzi/NS0ljgUn6ZBaXIJQieJwwpX75JalpTMgKgkE8RxMw5WyCRTlYxhWnjDtOGWdTAOEWJA0="*)(*]VB*)

We improved our synthesizer pipeline; now we have many instruments!

Sound[{
  SoundNote["C5", {0., .4}, "Piano"],
  SoundNote["D5", {.5, .9}, "Guitar"],
  SoundNote["E5", {1., 1.4}, "Violin"],
  SoundNote["F5", {1.5, 1.9}, "Flute"],
  SoundNote["G5", {2., 2.4}, "Trumpet"],
  SoundNote["A5", {2.5, 2.9}, "Organ"],
  SoundNote["B5", {3., 3.4}, "Marimba"],
  SoundNote["C6", {3.5, 3.9}, "Vibraphone"],
  SoundNote["C2", {4., 4.4}, "Drums"],

  SoundNote["C5", {4.5, 4.9}, "AMSynth"], (*WLJS only*)
  SoundNote["D5", {5., 5.4}, "FMSynth"], (*WLJS only*)
  SoundNote["E5", {5.5, 5.9}, "DuoSynth"], (*WLJS only*)
  SoundNote["F4", {6., 6.4}, "MembraneSynth"], (*WLJS only*)
  SoundNote["G5", {6.5, 6.9}, "MetalSynth"] (*WLJS only*)
}]
(*VB[*)(FrontEndRef["7e531460-c334-45bd-b1bd-94c806387878"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm6eaGhuamBnoJhsbm+iamCal6CYZAglLk2QLAzNjC3MgBAB4VxTu"*)(*]VB*)

Here is how you can write a basic 808-style beat:

With[{step = 60/140/2},
  Sound @ Join[
    (*BB[*)(* closed hats *)(*,*)(*"1:eJxTTMoPSmNhYGAo5gcSAUX5ZZkpqSn+BSWZ+XnFaYwgCS4g4Zyfm5uaV+KUXxEMUqxsbm6exgSSBPGCSnNSg9mAjOCSosy8dLBYSFFpKpoKkDkeqYkpEFXBILO1sCgJSczMQVYCAOFrJEU="*)(*]BB*)
    Table[
      SoundNote["C6", {t, t + .035}, "MetalSynth"],
      {t, 0, 15 step, step}
    ],

    (*BB[*)(* kick *)(*,*)(*"1:eJxTTMoPSmNhYGAo5gcSAUX5ZZkpqSn+BSWZ+XnFaYwgCS4g4Zyfm5uaV+KUXxEMUqxsbm6exgSSBPGCSnNSg9mAjOCSosy8dLBYSFFpKpoKkDkeqYkpEFXBILO1sCgJSczMQVYCAOFrJEU="*)(*]BB*)
    {
      SoundNote["C1", {0, .18}, "Drums"],
      SoundNote["C1", {3 step, 3 step + .18}, "Drums"],
      SoundNote["C1", {6 step, 6 step + .18}, "Drums"],
      SoundNote["C1", {11 step, 11 step + .18}, "Drums"],
      SoundNote["C1", {14 step, 14 step + .18}, "Drums"]
    },

    (*BB[*)(* snare / clap on beat 3 *)(*,*)(*"1:eJxTTMoPSmNhYGAo5gcSAUX5ZZkpqSn+BSWZ+XnFaYwgCS4g4Zyfm5uaV+KUXxEMUqxsbm6exgSSBPGCSnNSg9mAjOCSosy8dLBYSFFpKpoKkDkeqYkpEFXBILO1sCgJSczMQVYCAOFrJEU="*)(*]BB*)
    {
      SoundNote["C2", {4 step, 4 step + .12}, "Drums"],
      SoundNote["C2", {12 step, 12 step + .12}, "Drums"]
    },

    (*BB[*)(* sliding-feeling 808 bass line *)(*,*)(*"1:eJxTTMoPSmNhYGAo5gcSAUX5ZZkpqSn+BSWZ+XnFaYwgCS4g4Zyfm5uaV+KUXxEMUqxsbm6exgSSBPGCSnNSg9mAjOCSosy8dLBYSFFpKpoKkDkeqYkpEFXBILO1sCgJSczMQVYCAOFrJEU="*)(*]BB*)
    {
      SoundNote["C2", {0, 3 step}, "Marimba"],
      SoundNote["G1", {3 step, 4 step}, "Marimba"],
      SoundNote["Bb1", {6 step, 8 step}, "Marimba"],
      SoundNote["C2", {8 step, 11 step}, "Marimba"],
      SoundNote["Eb2", {12 step, 16 step}, "Marimba"]
    }
  ]
]
(*VB[*)(FrontEndRef["58c0d58d-07d4-4170-8757-1d1ae9932fe7"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm1okG6SYWqToGpinmOiaGJob6FqYm5rrGqYYJqZaWhobpaWaAwB4xxUj"*)(*]VB*)

Music Framework

In WL 15, a new framework was added for working with chords, music lines, and scales. You can find all the symbols under Music* using Names[].

In particular, we developed nice preview boxes for notes, pitches, chords, and scales. In general, it follows OOP-like patterns while remaining immutable. For example, here is a single chord:

MusicChord["CMajor7"]
MusicChord[(*VB[*) <|"Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Key" -> "C", "Accidental" -> 0|>]|> (*,*)(*"1:eJztVMtum0AUddv0lU2lbip11UplaYmMwbG7Y2ycNIrzYMAPVp3ANSEGxhmGGP9kv6Dqt/ROSFpv2oWXVSV0Zu45R6PDYcTHK+EtnrVarfI9AhX1SMj8qyMlLxKIWZXnXG6QXjzRnjcIIykK5RbxJJWq4lkjvN0S3BqiSvGrDNgnpM1DK+5ZC97umATaFu/Z7V6XQ3thRf1D3u0Ss9NvEuwhnKalao78NT3V07utdA+pvijIJyms2QvUzngOnz803n0EN06VkFr+fdqxyOJmeo0QeKdDiEQM7DtOBukZxKHUsCmuBunfE2TMb4Q0iMngDgp1jcyjhJ7BlhM35MDoOHBS+/5YXLD8LJ0fOcJOIuYEMzsMl6tbVhiEUjYNcZkVIz5fJwPfStxJzfLw4hYd0cz25ssM9WBzuQyS6jp1vBO68vPVelonk2AaAmRJGQ5SM7t0xjfzae0YnaF+dLatPPaQPrxK04puwKvwo7zSG+DxeZFt7llfVrBL554QatfOv/2588H/lh9bfqlvtlBQ7lrzj7/UbOG9djUcaaDWv1t7+Vz/nnhWwk9MHkqg"*)(*]VB*)]

Click on it

It shows you a tiny piano with notes entering the chord. You can extract a bass pitch, which is a MusicPitch object.

MusicChord[(*VB[*) <|"Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Key" -> "C", "Accidental" -> 0|>]|> (*,*)(*"1:eJztVMtum0AUddv0lU2lbip11UplaYmMwbG7Y2ycNIrzYMAPVp3ANSEGxhmGGP9kv6Dqt/ROSFpv2oWXVSV0Zu45R6PDYcTHK+EtnrVarfI9AhX1SMj8qyMlLxKIWZXnXG6QXjzRnjcIIykK5RbxJJWq4lkjvN0S3BqiSvGrDNgnpM1DK+5ZC97umATaFu/Z7V6XQ3thRf1D3u0Ss9NvEuwhnKalao78NT3V07utdA+pvijIJyms2QvUzngOnz803n0EN06VkFr+fdqxyOJmeo0QeKdDiEQM7DtOBukZxKHUsCmuBunfE2TMb4Q0iMngDgp1jcyjhJ7BlhM35MDoOHBS+/5YXLD8LJ0fOcJOIuYEMzsMl6tbVhiEUjYNcZkVIz5fJwPfStxJzfLw4hYd0cz25ssM9WBzuQyS6jp1vBO68vPVelonk2AaAmRJGQ5SM7t0xjfzae0YnaF+dLatPPaQPrxK04puwKvwo7zSG+DxeZFt7llfVrBL554QatfOv/2588H/lh9bfqlvtlBQ7lrzj7/UbOG9djUcaaDWv1t7+Vz/nnhWwk9MHkqg"*)(*]VB*)]["Bass"]
MusicPitch[(*VB[*) <|"Accidental" -> 0, "Key" -> "C"|> (*,*)(*"1:eJxTTMoPSmNmYGAolgISTvkVbvlFuQmORUWJeempKcGlubmJRZVA4TRGkBp+IOFWlJ9X4pqXEpZZVFKamAOREEKScK1ITS4tSUzKSQ1WAQonJ1kYGiWlJOumWhgn6ZqYmFvoWpiaJ+smm5iZWFiapqVZJBulMYAMYQESPpnFJcWsINMSc4pTAbwfKnE="*)(*]VB*)]

Using pitch and duration objects, you can construct a note.

MusicNote[MusicPitch[(*VB[*) <|"Accidental" -> 0, "Key" -> "C"|> (*,*)(*"1:eJxTTMoPSmNmYGAolgISTvkVbvlFuQmORUWJeempKcGlubmJRZVA4TRGkBp+IOFWlJ9X4pqXEpZZVFKamAOREEKScK1ITS4tSUzKSQ1WAQonJ1kYGiWlJOumWhgn6ZqYmFvoWpiaJ+smm5iZWFiapqVZJBulMYAMYQESPpnFJcWsINMSc4pTAbwfKnE="*)(*]VB*)], MusicDuration[1/4]]
MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "C"|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOwkAUxWfUjYkbE1ea2CWJHRge7toCKgGETsujK4d2qJW2A9M2lK808Yu8BRQ2LnRpMjlz7zk3NydnMjdjrk8OcrlcdAWg8rTBRfCiCEFDlzkkCQIqlkBP9rKZc4CG4GFcD52+J+KE+mvhYkeop8xOYjr2GbkF2qmUqljG5byMJixfrCKaHxdZKS/bqFytYNke49JkP1tyCNDyoni98rtbaZc77jaunmIW9D22IMegdXjM7q/Xs2cAdceLucjk7bZH7jvr7hTA1Fs1ZnOHkXfoJFSRkKKqElbhllB1RSCtCPDVAa3tiFAgWSoorJkaRpt3SdDxRg8Kx65NFHOILWs6m5NQQqpKBhZcw7BBRwtXM4puvZ+SwOrOYcIeYn009UE3l72p6SavnqI31ZkRzBaD1O2bA4sx340szbvze0r7bTRIFalQy07mbccPrqkb99tA9QTe4SQrGHWeQ3+5Yg2RsN/GnMVaSwSNPR7+NeqPn6OWJdT4x2lHR9kXoX7EPgFPSub+"*)(*]VB*)]

By grouping individual chords or notes, you can construct a MusicScore.

MusicScore[{
  MusicChord[(*VB[*) <|"Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Key" -> "C", "Accidental" -> 0|>]|> (*,*)(*"1:eJztVMtum0AUddv0lU2lbip11UplaYmMwbG7Y2ycNIrzYMAPVp3ANSEGxhmGGP9kv6Dqt/ROSFpv2oWXVSV0Zu45R6PDYcTHK+EtnrVarfI9AhX1SMj8qyMlLxKIWZXnXG6QXjzRnjcIIykK5RbxJJWq4lkjvN0S3BqiSvGrDNgnpM1DK+5ZC97umATaFu/Z7V6XQ3thRf1D3u0Ss9NvEuwhnKalao78NT3V07utdA+pvijIJyms2QvUzngOnz803n0EN06VkFr+fdqxyOJmeo0QeKdDiEQM7DtOBukZxKHUsCmuBunfE2TMb4Q0iMngDgp1jcyjhJ7BlhM35MDoOHBS+/5YXLD8LJ0fOcJOIuYEMzsMl6tbVhiEUjYNcZkVIz5fJwPfStxJzfLw4hYd0cz25ssM9WBzuQyS6jp1vBO68vPVelonk2AaAmRJGQ5SM7t0xjfzae0YnaF+dLatPPaQPrxK04puwKvwo7zSG+DxeZFt7llfVrBL554QatfOv/2588H/lh9bfqlvtlBQ7lrzj7/UbOG9djUcaaDWv1t7+Vz/nnhWwk9MHkqg"*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "C"|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOwkAUxWfUjYkbE1ea2CWJHRge7toCKgGETsujK4d2qJW2A9M2lK808Yu8BRQ2LnRpMjlz7zk3NydnMjdjrk8OcrlcdAWg8rTBRfCiCEFDlzkkCQIqlkBP9rKZc4CG4GFcD52+J+KE+mvhYkeop8xOYjr2GbkF2qmUqljG5byMJixfrCKaHxdZKS/bqFytYNke49JkP1tyCNDyoni98rtbaZc77jaunmIW9D22IMegdXjM7q/Xs2cAdceLucjk7bZH7jvr7hTA1Fs1ZnOHkXfoJFSRkKKqElbhllB1RSCtCPDVAa3tiFAgWSoorJkaRpt3SdDxRg8Kx65NFHOILWs6m5NQQqpKBhZcw7BBRwtXM4puvZ+SwOrOYcIeYn009UE3l72p6SavnqI31ZkRzBaD1O2bA4sx340szbvze0r7bTRIFalQy07mbccPrqkb99tA9QTe4SQrGHWeQ3+5Yg2RsN/GnMVaSwSNPR7+NeqPn6OWJdT4x2lHR9kXoX7EPgFPSub+"*)(*]VB*)],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/8|>], "Name" -> "Major", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "D"|>]|> (*,*)(*"1:eJztVMtO20AUTUsftJtKbJBYUYlZRoLJmBB24zyAiFDw2Hl41bE9OCa2J4xtxflJFvwEv8GdmEc27SLLqpJ17txzrkbnHlv+6UnrdqtWq2V7AKYse1Ilv6lSPA1FwIok4WoJ9O0HPfMDoKdkmnfTYBipvOBxJeysCd1S+EXOvViwA6AJaREumkd1jA1SJ6TZqHsebtY5bwTHvtH0/BapHHwCuIyyvLryrfuou901dy+uLnKRDCOxYF9Au+KJON2vZr8DdIMol0rL77edyzioum8AjnXZEb4MBHuEDuEThKlpIsOEinBrReABv5MK6isBSntNhwM+Qg0q+qVtD+Q1S66iyRmVRugz6owN153N71mKsGmykQtlnPb4ZBG2bRJ2hyVL3Ot7mPDHhjWZxaA7y5uZExbTiFp9c24n88WoDIfOyBUiDjO3HR3GN3RwNxmVFDU6+tHe1vwYHfNlgSoLvbdVwKvY1gfBg19pvFyxtirEJklbUuabJv3w56Q7/1N+Tfmr/p5lLrJNY376S8wE4cMeMtrAsylXc9hFU5T8u/lnn/XficeZeAYsf0oF"*)(*]VB*)],
  MusicTransform[MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "C"|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOwkAUxWfUjYkbE1ea2CWJHRge7toCKgGETsujK4d2qJW2A9M2lK808Yu8BRQ2LnRpMjlz7zk3NydnMjdjrk8OcrlcdAWg8rTBRfCiCEFDlzkkCQIqlkBP9rKZc4CG4GFcD52+J+KE+mvhYkeop8xOYjr2GbkF2qmUqljG5byMJixfrCKaHxdZKS/bqFytYNke49JkP1tyCNDyoni98rtbaZc77jaunmIW9D22IMegdXjM7q/Xs2cAdceLucjk7bZH7jvr7hTA1Fs1ZnOHkXfoJFSRkKKqElbhllB1RSCtCPDVAa3tiFAgWSoorJkaRpt3SdDxRg8Kx65NFHOILWs6m5NQQqpKBhZcw7BBRwtXM4puvZ+SwOrOYcIeYn009UE3l72p6SavnqI31ZkRzBaD1O2bA4sx340szbvze0r7bTRIFalQy07mbccPrqkb99tA9QTe4SQrGHWeQ3+5Yg2RsN/GnMVaSwSNPR7+NeqPn6OWJdT4x2lHR9kXoX7EPgFPSub+"*)(*]VB*)], {"Transpose", 7}],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/8|>], "Name" -> "Major", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "A"|>]|> (*,*)(*"1:eJztVM9v0zAULr9hFyQuSJxAwsdKw43blZvdpsC0ji1O+iMnnNjLsiZx5yRq+k9y4J/g3+B52aCXcegRIVnf8/u+J+u9z5bfRdq7eNTpdMo3AEw3E23yb9QYUSRK8jrPhdkCffHA1rwEmBhdVG4hZ6mpapG1wqsdwW1UXFciyhR/D/TgSAxkD8su6UvVdWLidCPp9LuqHw0wGQ4i1R+2HTwGOEnLqj3yd/bQZq93urvt6kul8lmqNvwpaKciVx/ftrUHAK5MK22s/Oe0zzqTbfYCIPBOxirWUvEfkCF8hDBlDBEGEeHhDYGn4kobiHcEKKMdHTb4A+pRddz4/lSf8fw0XX6imiQxp8GChOFqfc0LhBnj8xDCopiI5SYZ+U7izhqeh2fXUBEviLdcZaAH2/NVkNSXKfWO2drP15t5k8yCeahUlpThKD3Mzun0ajlvKOqN7bK97fRDxux2gNYLO7dXw1U8txsl5Nci296wvqnVPk57Wlf7Ov39fqfpf5fvXH5m37OuVLmvzT//YrOD8OEIEViMXwqzhlkIUC75d/0vn9jfSWSl+gVP30p8"*)(*]VB*)],
  MusicRest[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJxlkEFPAjEQhVfRRLhwNOGkh/0BbiRZuLEsjSYEk13FK4UOpkm3k8y2Uf690102Clxe533z0tf0cYvFvhdFUT1iyfBHIFWbGZG0X6BKX1WSDoz3VyEzZBGE1i2sWmtyXpp2ccPygkY1wwot/OGlrt2Zuw7u/l/fsefVQbXW8F0OeJd7kk6jnT60+cAWSjukEDmtbV2f5aNY5rBDBeUnuzhJ42Qmsnic8RknkwakT93EaN4txAVJn5uji45zcbylfVAoL7yB8i4MINWbNYeGvpOH+jb8lTQ1/AJZ8VKa"*)(*]VB*)],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/2|>], "Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "F"|>]|> (*,*)(*"1:eJztVMtu00AUDc9SNkhskFiBVC8jpX6UwG6cOIWq6cNj5+EVE8+168b2pONx4/wkX4D4Fu7ULWQDiywRknVm7jlHo+Pjkd8vhJ886XQ61VsEVzQjIYuvREpWpsBpXRRMbpBOHmnPK4SRFKXySj7JpKpZ3gqvtwSvgbhWbJEDPUD6Q58zODJ59+jQtro27/W7LLF7XTthicVjMJOF1SZ4inCaVao98tf0WE9vttLdp/qioJhksKbPUTtjBXx613pfIng8U0Jq+fdpn0XO22kfIfRPhxALDvQ7TobZN0ziuobj4mqYH+8Ic8yuhTTMHoVbKNUVMg8SegZbTtyYh4ZF4KQJgrG4oMVZNj8mwkljSsKZE0XL1Q0tDdN16TTCZVaO2HydDgI79SYNLaKLG3TEM8efL3PUw83lMkzrq4z4J+4qKFbraZNOwmkEkKdVNMh6+SUZX8+nDTGsoX50tq08ztC9f5W2Fd2AX+NHeaE3wPh5mW/u2EDWsEvnvhBq186//bnz0f+WH1re0zdbKKh2rfnHX2q28V4TDQMHwXP+3dqrZ/r3xPIKfgI7EEuE"*)(*]VB*)]
}]
(*VB[*)(FrontEndRef["572c90bd-88cd-4175-ba26-b25edb2060f9"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5AIzi/Ng3KFgIRbUX5eiWteimtFanJpSWJSTmqwClDY1Nwo2dIgKUXXwiI5RdfE0NxUNynRyEw3ycg0NSXJyMDMIM0SAJOJGIk="*)(*]VB*)

A great thing about WL's Music Framework is that it has all the necessary functions to transpose and invert chords out of the box. In the past, we had to build our own on top of SoundNote. Multiple voices can also be added using a MusicVoice container.

music = MusicScore[{
 MusicVoice[{
  MusicChord[(*VB[*) <|"Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Key" -> "C", "Accidental" -> 0|>]|> (*,*)(*"1:eJztVMtum0AUddv0lU2lbip11UplaYmMwbG7Y2ycNIrzYMAPVp3ANSEGxhmGGP9kv6Dqt/ROSFpv2oWXVSV0Zu45R6PDYcTHK+EtnrVarfI9AhX1SMj8qyMlLxKIWZXnXG6QXjzRnjcIIykK5RbxJJWq4lkjvN0S3BqiSvGrDNgnpM1DK+5ZC97umATaFu/Z7V6XQ3thRf1D3u0Ss9NvEuwhnKalao78NT3V07utdA+pvijIJyms2QvUzngOnz803n0EN06VkFr+fdqxyOJmeo0QeKdDiEQM7DtOBukZxKHUsCmuBunfE2TMb4Q0iMngDgp1jcyjhJ7BlhM35MDoOHBS+/5YXLD8LJ0fOcJOIuYEMzsMl6tbVhiEUjYNcZkVIz5fJwPfStxJzfLw4hYd0cz25ssM9WBzuQyS6jp1vBO68vPVelonk2AaAmRJGQ5SM7t0xjfzae0YnaF+dLatPPaQPrxK04puwKvwo7zSG+DxeZFt7llfVrBL554QatfOv/2588H/lh9bfqlvtlBQ7lrzj7/UbOG9djUcaaDWv1t7+Vz/nnhWwk9MHkqg"*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "C"|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOwkAUxWfUjYkbE1ea2CWJHRge7toCKgGETsujK4d2qJW2A9M2lK808Yu8BRQ2LnRpMjlz7zk3NydnMjdjrk8OcrlcdAWg8rTBRfCiCEFDlzkkCQIqlkBP9rKZc4CG4GFcD52+J+KE+mvhYkeop8xOYjr2GbkF2qmUqljG5byMJixfrCKaHxdZKS/bqFytYNke49JkP1tyCNDyoni98rtbaZc77jaunmIW9D22IMegdXjM7q/Xs2cAdceLucjk7bZH7jvr7hTA1Fs1ZnOHkXfoJFSRkKKqElbhllB1RSCtCPDVAa3tiFAgWSoorJkaRpt3SdDxRg8Kx65NFHOILWs6m5NQQqpKBhZcw7BBRwtXM4puvZ+SwOrOYcIeYn009UE3l72p6SavnqI31ZkRzBaD1O2bA4sx340szbvze0r7bTRIFalQy07mbccPrqkb99tA9QTe4SQrGHWeQ3+5Yg2RsN/GnMVaSwSNPR7+NeqPn6OWJdT4x2lHR9kXoX7EPgFPSub+"*)(*]VB*)],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/8|>], "Name" -> "Major", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "D"|>]|> (*,*)(*"1:eJztVMtO20AUTUsftJtKbJBYUYlZRoLJmBB24zyAiFDw2Hl41bE9OCa2J4xtxflJFvwEv8GdmEc27SLLqpJ17txzrkbnHlv+6UnrdqtWq2V7AKYse1Ilv6lSPA1FwIok4WoJ9O0HPfMDoKdkmnfTYBipvOBxJeysCd1S+EXOvViwA6AJaREumkd1jA1SJ6TZqHsebtY5bwTHvtH0/BapHHwCuIyyvLryrfuou901dy+uLnKRDCOxYF9Au+KJON2vZr8DdIMol0rL77edyzioum8AjnXZEb4MBHuEDuEThKlpIsOEinBrReABv5MK6isBSntNhwM+Qg0q+qVtD+Q1S66iyRmVRugz6owN153N71mKsGmykQtlnPb4ZBG2bRJ2hyVL3Ot7mPDHhjWZxaA7y5uZExbTiFp9c24n88WoDIfOyBUiDjO3HR3GN3RwNxmVFDU6+tHe1vwYHfNlgSoLvbdVwKvY1gfBg19pvFyxtirEJklbUuabJv3w56Q7/1N+Tfmr/p5lLrJNY376S8wE4cMeMtrAsylXc9hFU5T8u/lnn/XficeZeAYsf0oF"*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "G", "MIDINumber" -> 67|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOg0AUrc+oGxM3Jq40kWUTyqMRd9CXNm1tGeiDlVNmQCww7QAp/UoTv8hLW7UbF7o0mZy595ybm5MzmZspM72DUqmUXAEYLG8yHj3rnOPYpwRlUYT5Cmhvr5g5B2hyFqeNmAwDnmY43AgXO0Ijp26W4mlI0S3Qolh1CRG1MpYVt6zIVCprmiuWPYV4ckXzFHla9faLJYcAnSBJNyu/urV2ueNu6+oxpdEwoEt0DFqPpfT+ejN7BtAgQcp4IX9ve2Ah2XSnALbZqVOXEYreoBOkO0HSDUNQDbgFSVsTUksB+OyAru2IUEgVQdZpO7esLuujqBdMWjpTfRfp9lh1nNl8gWJBMgw0cuAax008Wfo1S/EbwxxFTn8BE+5YNSezEHR7NZjZfvYS6GbbmFvRfDnK/aE9cigN/cSpBWI40Luvk1GuC3K9OIW3HT9q3di6/w7UzOAdToqCYvIUh6s1a/GM/jbmItZ6xnEasPivUb//HHVFkJr/OO3kqPgiOEzoB9Mj54A="*)(*]VB*)],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/8|>], "Name" -> "Major", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "A"|>]|> (*,*)(*"1:eJztVM9v0zAULr9hFyQuSJxAwsdKw43blZvdpsC0ji1O+iMnnNjLsiZx5yRq+k9y4J/g3+B52aCXcegRIVnf8/u+J+u9z5bfRdq7eNTpdMo3AEw3E23yb9QYUSRK8jrPhdkCffHA1rwEmBhdVG4hZ6mpapG1wqsdwW1UXFciyhR/D/TgSAxkD8su6UvVdWLidCPp9LuqHw0wGQ4i1R+2HTwGOEnLqj3yd/bQZq93urvt6kul8lmqNvwpaKciVx/ftrUHAK5MK22s/Oe0zzqTbfYCIPBOxirWUvEfkCF8hDBlDBEGEeHhDYGn4kobiHcEKKMdHTb4A+pRddz4/lSf8fw0XX6imiQxp8GChOFqfc0LhBnj8xDCopiI5SYZ+U7izhqeh2fXUBEviLdcZaAH2/NVkNSXKfWO2drP15t5k8yCeahUlpThKD3Mzun0ajlvKOqN7bK97fRDxux2gNYLO7dXw1U8txsl5Nci296wvqnVPk57Wlf7Ov39fqfpf5fvXH5m37OuVLmvzT//YrOD8OEIEViMXwqzhlkIUC75d/0vn9jfSWSl+gVP30p8"*)(*]VB*)],
  MusicRest[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJxlkEFPAjEQhVfRRLhwNOGkh/0BbiRZuLEsjSYEk13FK4UOpkm3k8y2Uf690102Clxe533z0tf0cYvFvhdFUT1iyfBHIFWbGZG0X6BKX1WSDoz3VyEzZBGE1i2sWmtyXpp2ccPygkY1wwot/OGlrt2Zuw7u/l/fsefVQbXW8F0OeJd7kk6jnT60+cAWSjukEDmtbV2f5aNY5rBDBeUnuzhJ42Qmsnic8RknkwakT93EaN4txAVJn5uji45zcbylfVAoL7yB8i4MINWbNYeGvpOH+jb8lTQ1/AJZ8VKa"*)(*]VB*)],
  MusicChord[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/2|>], "Name" -> "Major Seventh", "Root" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "F"|>]|> (*,*)(*"1:eJztVMtu00AUDc9SNkhskFiBVC8jpX6UwG6cOIWq6cNj5+EVE8+168b2pONx4/wkX4D4Fu7ULWQDiywRknVm7jlHo+Pjkd8vhJ886XQ61VsEVzQjIYuvREpWpsBpXRRMbpBOHmnPK4SRFKXySj7JpKpZ3gqvtwSvgbhWbJEDPUD6Q58zODJ59+jQtro27/W7LLF7XTthicVjMJOF1SZ4inCaVao98tf0WE9vttLdp/qioJhksKbPUTtjBXx613pfIng8U0Jq+fdpn0XO22kfIfRPhxALDvQ7TobZN0ziuobj4mqYH+8Ic8yuhTTMHoVbKNUVMg8SegZbTtyYh4ZF4KQJgrG4oMVZNj8mwkljSsKZE0XL1Q0tDdN16TTCZVaO2HydDgI79SYNLaKLG3TEM8efL3PUw83lMkzrq4z4J+4qKFbraZNOwmkEkKdVNMh6+SUZX8+nDTGsoX50tq08ztC9f5W2Fd2AX+NHeaE3wPh5mW/u2EDWsEvnvhBq186//bnz0f+WH1re0zdbKKh2rfnHX2q28V4TDQMHwXP+3dqrZ/r3xPIKfgI7EEuE"*)(*]VB*)]
 }, "Instrument"->"Piano"],
 
 MusicVoice[{
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "C", "MIDINumber" -> 48|>], "Duration" -> MusicDuration[<|"Duration" -> 1/2|>]|> (*,*)(*"1:eJzdU8lOwzAQLauACxIXJE4gkWOl1sVst6QLiwqUOOmSE248CaFJXJxETb8SiS9i0hbohQMckaznmfdGo6dn+WgoTW+tVColBwiGzFtSRU+6Ujz2QbAsiriaIu2tFDO7CC0l47QZi26g0oyHc2FvSWjm4GYpH4bAjpF2yTmlgpBy9RRE+aRGz8pcQLXsDV1+Ss8uKi543mqxZB2hHSTpfOVXN9P2l9wtXN2kEHUDmLBN1O5lCpeH89kdhKYIUqkK+XvbtQzFvNtGsM12A1wpgL1hp5FzjeiGoVEDb41czAhSryF8dkjXl0QsSFWr6XCbW9ad7LDoPhhc6ZL6LtPtPnWc0fiVxRoxDNZz8OrHLT6Y+HXrxG92cxY5nVeccPvUHIxC1O3p48j2s+dAN2+NsRWNJ73c79o9ByD0E6ceVMJH/e5l0Mt1rdYoTuFtyQ9tGAv334GaGb7DVlEAFw9xOJ2xlsrgtzEXsTYyxdNAxn+N+v3nqKsaaZH/m3ayUXwRHibwAem0590="*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "D", "MIDINumber" -> 50|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU8tOg0AUrc+oGxM3Jq40kWUTHRht3UEBtWl9MEBbVk5hilhg2gFS+pUmfpGX1kc3LnRpMjlz7zk3NydnMidDbo02arVadgSg8dLkInlShaBpyAJSJAkVc6BHa9XMPoApeJobaeBGIi9ovBQOVgSjZH6R02HMyCnQF0MZ0Uu/WccybdSVC4TrDeVSqftNKqOGLGMcsNF6tWQToBNl+XLlV7fQDlfcfbi6zVniRmxGtkG74zm7Ol7O7gEYQZRzUcnf2254HCy7XQDH6ujM5wEjr9BJqCEhVdMkrMEtoeaCQLoM8NkB3VoRoUDnkqyydmnbXf5AkrtocK1yHPpEdfrY88aTKUklpGmk58HVT006mIUtWwkNtySJ9zCFCb+PrcE4Bt2ZP46dsHiOVKutTexkMuuVoev0PMbiMPNa0Vn8qHZfBr1SlWS9OpW3FT9Y1z7cfwdqFfAOO1XBaHCfxvMFa4uC/TbmKla9EDSPePrXqN9+jvpcQqbyf9POtqovQuOMvQO33ObL"*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "G", "MIDINumber" -> 55|>], "Duration" -> MusicDuration[<|"Duration" -> 1/8|>]|> (*,*)(*"1:eJzdU8tOg0AUrc+oGxM3Jq40kSVJO0BS3EFf2rS1ZaAPVk5hilhg2gFS+pUmfpGXtlo2LnRpMjlz7zk3NydnMndTZsyOSqVSfAOgs6zJePiicU4ij7o4DUPC10DPDvKZS4AmZ1HSiNyhz5OUBFvhqiA0MuqkCZkGFN8DLRGVqFIFia4ylURZUsuiSpWpSJyyiyRZlRUZzQ7zJccAHT9Otiu/u412XXC3c/WU0HDo0xU+Ba3HEvpwu529AGi4fsJ4Lu+3PbLA3XbnAJbRqVOHuRS/QyegqoA0XRcUHW4BqRsCtSSArw7oWkGEAlUESaPtzDS7rI/Dnj9paUzxHKxZY8W254sljgSk63hkwzWOmmSy8mqm7DWGGQ7t/hImnLFiTOYB6NZ6MLe89NXXjLa+MMPFapR5Q2tkUxp4sV3zy8FA675NRpkmSPX85N4KfpS6vnO/D9RI4R3O8oIS9zkK1hvW5Cn9bcx5rPWUk8Rn0V+j/vg56oqAmtX/m3Z8kn8REsT0Exm+5u4="*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "A", "MIDINumber" -> 57|>], "Duration" -> MusicDuration[<|"Duration" -> 1/8|>]|> (*,*)(*"1:eJzdU8lOwzAQLauACxIXJE4gkWOlLA203JwuLGIpcdIlJ9xkEkKTuDiJmn4lEl/EpAXaCwc4IlnPM++NRk/P8smIm/5GpVJJjxAMXnS4iJ+IECwJwKN5HDMxQ9pfK2f2ETqCJ1k78XqhyHIWLYSDFaFdgJtnbBQBPUVaBrk+YqBVNd/3qzUAqDYURa7CeW105ssNRVNdf71csolwG6bZYuV3N9cOV9x9urrOIO6FMKXbqN3zDC6OF7N7CG0vzLgo5eW2Kx55i24XwTZvW+ByD+gbdpJal1RiGJJu4C2pjTmhEg3hq0O6uSJioSqSRuCmsKw73qXxfTi8JFwPXErsge4448krTSTVMGjfwWuQdNhwGjStWtDuFTR2uq844Q50cziOULdnj2M7yJ9DYt4YEyueTPtF0LP7DkAUpE4zlKNHcvcy7BdE0lrlKb2t+NFbxqf7ZaBmju+wUxbAvIckms1ZS+Tw25jLWFu5YFnIk79G/f5z1Iqkdur/N+10q/wiLErhA+Sg59o="*)(*]VB*)],
  MusicRest[(*VB[*) <|"Duration" -> MusicDuration[<|"Duration" -> 1/8|>]|> (*,*)(*"1:eJxlkM1LxDAQxesXqBePgif30D/AQqF6a+0GhWWFRrvXjWYqgTQD0wTd/95Ju8Wvy8u83zzyQhav2HRHSZIMVywVfgqkflsSKfcOWoa+V7Rj3B3EzAWLIHR+6XRryAdlp8UxywNaPQ5rdPCNV2bwf9xhdJc/+vY9jx761sCHPOddHUh5g+7uespHttTGI8XI79rJnbG8NKsa3lCD3LBLsyLNSlGlecVnmt2OoLiZJ0b380L8I0UxHnM0r8X+lulBsbwJFuRpHEDpJ2d3I32mAMNJ/CtlB/gCWwlSng=="*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "D", "Name" -> "D"|>], "Duration" -> MusicDuration[<|"Duration" -> 1/8|>]|> (*,*)(*"1:eJzdU01PwkAUxM+oFxMvJp40sUcSWNpYvLW0qAQQui0fPbltl1ppu7BtQ/mVJv4iXwGFiwc9mmxm35t5eZnMZm8cZkwOSqVScgWgsrzJePSicE5in3o4iyLCl0BP9oqZc4AmZ3Gqx94g4GlGwrVwsSPoOXWzlDghxbdAy4g4d3RSLxNHcsqi7FTKpOrUy4ggt+aIqCa68mS/WHII0A6SdL3yu1tplzvuNq6eUhoNArrAx6B1WUrvr9ezZwC6F6SMF/J22yMLvXV3CmAZbY26zKP4HToByQJSVFWQVLgFVF8RSBMBvjqgGzsiFKgq1BTayk2zw3o46gbjB4VJvosVayTZ9nQ2x7GAVBUPbbhGcZOMF37DFH19kOPI7s1hwh1Jxngagm4t+1PLz14DxWipMzOaLYa5P7CGNqWhn9iNoBL2lc7beJgrQk0rTuFtx4+kqRv320CNDN7hpCgo8Z7jcLliTZ7R38ZcxKplnKQBi/8a9cfPUVcF1JT/b9rJUfFFSJjQT9Jp59U="*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "D", "MIDINumber" -> 38|>], "Duration" -> MusicDuration[<|"Duration" -> 1/2|>]|> (*,*)(*"1:eJzdU01PwkAUxM+oFxMvJp40sUcSWaiCt5aCSgCh2/LRk9vutlbaLmzbUH6lib/IV0Dh4kGPJs3szszLy2Q2vbK57u4VCoX4AkDlWZOL8EURgkQeozgNQyIWILs7+cwpQFPwKGlEdOCLJCXByjjbMhoZc9KE2AHD1yATSkvEJXfFagWxYgXZqGhToOiuWr11bKdGaq67my/ZB2j7cbJa+c2W3vlWunWqp4SFA5/N8SF4XZ6w+8vV7AlAg/oJF7m92fbIA7pixwCm3taYwynD78AkVJWQoqqSrMIpodpSQBoC+GIg17dMuKCSVFZYKzOMDu/hsOuPHxQuew5WzJFsWZPpDEcSUlU8tOAYRU0ynnt1o+I1BhkOrd4MJpyRrI8nAfjmoj8xvfTVV/SWOjXC6XyYeQNzaDEWeLFV92+CvtJ5Gw8zRSpr+Zdn28oja+o6/aZQPYV3OMovjNDnKFgsVUOk7Lc157VqqSCJz6O/Vv3xc9UlCTX/cdvxQf6LkCBmnxqT6D8="*)(*]VB*)],
  MusicNote[(*VB[*) <|"Pitch" -> MusicPitch[<|"Accidental" -> 0, "Key" -> "D", "MIDINumber" -> 38|>], "Duration" -> MusicDuration[<|"Duration" -> 1/4|>]|> (*,*)(*"1:eJzdU01PwkAUxM+oFxMvJp40sUcSWaiCt5aCSgCh2/LRk9vutlbaLmzbUH6lib/IV0Dh4kGPJs3szszLy2Q2vbK57u4VCoX4AkDlWZOL8EURgkQeozgNQyIWILs7+cwpQFPwKGlEdOCLJCXByjjbMhoZc9KE2AHD1yATSkvEJXfFagWxYgXZqGhToOiuWr11bKdGaq67my/ZB2j7cbJa+c2W3vlWunWqp4SFA5/N8SF4XZ6w+8vV7AlAg/oJF7m92fbIA7pixwCm3taYwynD78AkVJWQoqqSrMIpodpSQBoC+GIg17dMuKCSVFZYKzOMDu/hsOuPHxQuew5WzJFsWZPpDEcSUlU8tOAYRU0ynnt1o+I1BhkOrd4MJpyRrI8nAfjmoj8xvfTVV/SWOjXC6XyYeQNzaDEWeLFV92+CvtJ5Gw8zRSpr+Zdn28oja+o6/aZQPYV3OMovjNDnKFgsVUOk7Lc157VqqSCJz6O/Vv3xc9UlCTUr/7ft+CD/RUgQs08cXehB"*)(*]VB*)]
 }, "Instrument"->"Organ"]
}]
(*VB[*)(FrontEndRef["ebbba9e5-8e53-4079-8509-45bc699c39c8"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5AIzi/Ng3KFgIRbUX5eiWteimtFanJpSWJSTmqwClA4NSkpKdEy1VTXItXUWNfEwNxS18LUwFLXxDQp2czSMtnYMtkCAJwHGHk="*)(*]VB*)

Neat!

There is backward compatibility: you can wrap it in Sound and use it in EmitSound, for example:

EmitSound[music//Sound]
music//Sound
(*VB[*)(FrontEndRef["cdbc4a8e-bab1-4d24-bf7b-9b95f209cff4"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJ6ckJZskWqTqJiUmGeqapBiZ6CalmSfpWiZZmqYZGVgmp6WZAACgDhbD"*)(*]VB*)

If you need scales, there is a special symbol for that:

MusicScale@RandomChoice@MusicScale["Names"]
MusicScale[(*VB[*) <|"Name" -> "Dorian"|> (*,*)(*"1:eJzdU8tu2zAQdJqmSYoCAXop0FMCREcDrmwmUW+kHmmDPEXJD51CSbSqmhIdSoLl7+wPdWnnYaDoIT0WEIa7M4PFcggdxdKfbnc6neozAJGtJ1Vxj5ViZcZT2hQFU0ugp1vacwDgKVnWbpkOc1U3TKyFjxuC2/KkqVksOD0Gum+xk2kS8y47YafdgdWLuzFHVjfppciMz/oWOh1M3+ghbwEu86pej3zuVtqnje0et/pe82KY8wV9B9o1K/jXw7X3PYCb5rVUWn6Z9k2KdN3tA4T+pcMTmXL6CzrDPDNMTIiBCJyGaa0I05EqZyUUTwxI9oYBCvOL0cf8og2CK3lLi+t8co4lyhKKwzGKotn8gcIAQugogmNcemyyyOxgkLnDlhbR7QM4kjHyJzMBeri8m4VZ8yPH/gWZB8V8MWqzYTiKOBdZFdl5T9zhq5+TUYuNvqM/vdvGPsghjzd4CdVv4C32dMFZelOK5YoNVMNfG/WujlrWvPrXrD9s/TVre2CYPUeDayAbRE+wGu6jGU/DuQasgfxhsNH/+0jVjv67mKj4bydm9LM="*)(*]VB*)]

Construct chords by sampling every third pitch in the list:

pitches = MusicScale[(*VB[*) <|"Name" -> "Dorian"|> (*,*)(*"1:eJzdU8tu2zAQdJqmSYoCAXop0FMCREcDrmwmUW+kHmmDPEXJD51CSbSqmhIdSoLl7+wPdWnnYaDoIT0WEIa7M4PFcggdxdKfbnc6neozAJGtJ1Vxj5ViZcZT2hQFU0ugp1vacwDgKVnWbpkOc1U3TKyFjxuC2/KkqVksOD0Gum+xk2kS8y47YafdgdWLuzFHVjfppciMz/oWOh1M3+ghbwEu86pej3zuVtqnje0et/pe82KY8wV9B9o1K/jXw7X3PYCb5rVUWn6Z9k2KdN3tA4T+pcMTmXL6CzrDPDNMTIiBCJyGaa0I05EqZyUUTwxI9oYBCvOL0cf8og2CK3lLi+t8co4lyhKKwzGKotn8gcIAQugogmNcemyyyOxgkLnDlhbR7QM4kjHyJzMBeri8m4VZ8yPH/gWZB8V8MWqzYTiKOBdZFdl5T9zhq5+TUYuNvqM/vdvGPsghjzd4CdVv4C32dMFZelOK5YoNVMNfG/WujlrWvPrXrD9s/TVre2CYPUeDayAbRE+wGu6jGU/DuQasgfxhsNH/+0jVjv67mKj4bydm9LM="*)(*]VB*)]["PitchList"];
pitches = 
  Join[pitches, 
   MusicTransform[{"Transpose", 12}] /@ Rest@pitches];

Table[MusicChord@pitches[[{i, i + 2, i + 4, i + 6}]], {i, 4}]//TableForm
(*GB[*){{MusicChord[(*VB[*) <|"PitchList" -> {MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "C"|>], MusicPitch[<|"Accidental" -> -1, "Octave" -> 4, "Key" -> "E"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "G"|>], MusicPitch[<|"Accidental" -> -1, "Octave" -> 4, "Key" -> "B"|>]}|> (*,*)(*"1:eJxdUclOwzAQLZtYLkhckDiBhI+V2ixtwy1OUxZBaeOkS06YxgmhTlyyqOmfc2RMQFRI1puZ92ZG45mrV+GEe41GI78AwKIaiCx5MbOMphELSJkkNNsAHe7InFOAQSbSwk6DSZwVJeW1cLYl2BVblAV95YxcA93TjK7abatNqoW9phYCGIZmNLud0GipYYf1Op26yT7AY5wX/6JdGZ1vTfcz1X3BkknM1uQQtKEoWH5zWSefANhBXIhM6n/t7gQP6ugYwHMe+2whAkY+IUJKDykmxkjHYJFifBOKpSGlZSPdAn7AaYH0vmRuJeD/tPJbBvXWVhdwlDZSTfZQue6TGJFkGM9vTaFHC2J6M933l6sPkiIFYzL1wczSAZ2vI8vVIntSkcQffUDGYqY78yUH3duMl15UvsWm84BXbrJaT6to4k19xniU+1bc4mPz6X0+rUyk9uWTs23No/fxzzfrjcntOCVc7Eg6jAbPKd98s25WsvxAnpfynH0Bl7CRag=="*)(*]VB*)]}(*||*),(*||*){MusicChord[(*VB[*) <|"PitchList" -> {MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "D"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "F"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "A"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 5, "Key" -> "C"|>]}|> (*,*)(*"1:eJxdUdtOwkAQxVu8vJj4YuKTJvaxCfQi1LdtAZUAQrfl0idLd1srbRd6CeVn/RZnKUZisjkzc87JZHbmYcFM/6RWq2V3ADoruyyNP1CauklACS7i2E23QPtH3HMN0E1ZkncSMgnTvHCjSrg5EDol9YrcXUQUPwLtE/Lkq4uG6BPNFxVF9sSF1iRiU2lStaU1WrKrVU1OAfphlv+rjnl1ezDdfqq3nMaTkG7wOWhDltPs+b4yXwF0SJizlOt/7V5ZRKrqEsA2+23qMULxN1SC1BIkpOuCqkMUJG1HSG1FkOpdDoiDoQL3K4LLOPBCIjUEGdFeaVkDNsLxMJy/IKYGHkb2THWc5WqNE0HSdTx1IMySrjvfBIalBJ1JiWNntAaHN1PN+TIC3d6Ol3ZQfIbI7OkrK15tpmUwsacOpVGQOUZYj8Zo8DWflkiQ2/zx2Q7mUdv6/jPVXvgOzALucsET6pL3JNruWCstaHbGj+hGGf0BNMGLnA=="*)(*]VB*)]}(*||*),(*||*){MusicChord[(*VB[*) <|"PitchList" -> {MusicPitch[<|"Accidental" -> -1, "Octave" -> 4, "Key" -> "E"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "G"|>], MusicPitch[<|"Accidental" -> -1, "Octave" -> 4, "Key" -> "B"|>], MusicPitch[<|"Accidental" -> 0, "Key" -> "D", "MIDINumber" -> 74|>]}|> (*,*)(*"1:eJxlUclOwzAQLZtYLkhckDiBhI+VwK1F4RY3KYtY46RLTjiJE0KduM2ipn/OkTEBUYFkvZl5b2Y0njnxlR1ttFqt4giAqnqg8vTVyHOexSJkVZryfAl0tKZz9gEGucpKKwuHSV5WXDbCwYpg1SKoSu5LwU6B5pxHUS/stCMcXbS73Sho86DD2xwT/7zn48gPeNNkE+A+Kco/0bqODlem+57qthTpMBELtg3aoypFcXXcJO8BWGFSqlzrv+1ulAybaBfAte9NEahQsA+IEO4hbFCKCAWL8OUXgS1E+kANJC8RMbsIn11roP9ok0D2TxnU91e6gIPPUccQd7XjPKhnlj4mk2tDkThghjsmnjedzVmGMKVs5IEZZwM+WcR9pxtbw5ql3vMcMoIxsSdTCbq7fJm6cfWWGPYdnTnpbDGq46E78oSQceH1kzP5Yjy8T0a1gTqmfnq2lXmISb+/2WxMb8eu4GI72hE8fMrk8ot18koUW/q8XBbiE2FGk5M="*)(*]VB*)]}(*||*),(*||*){MusicChord[(*VB[*) <|"PitchList" -> {MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "F"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 4, "Key" -> "A"|>], MusicPitch[<|"Accidental" -> 0, "Octave" -> 5, "Key" -> "C"|>], MusicPitch[<|"Accidental" -> -1, "Key" -> "E", "MIDINumber" -> 75|>]}|> (*,*)(*"1:eJxdUVtP2zAU7gZoGy9Ie0Ha0ybNj5VaN4Z2b06awBCUEie95GnGcUNWJ25zUdPfzJ/gmDCtQrI+n+9yrGP7x6P2V0edTqf8BmDrxtNF9ocWBc8TGbM6y3ixB3n1wWTOALxC55Wbx7O0qGquWuPrgeE2UtQVf1SS/QR5IIhYWb1hNx5erLrWJZbdkRjyruhfiFF8yUWfWO0hxwC3aVm9Yx8NOz+Y7m2q35XMZqncsU/gTXQly1/f2/ApgBunlS6M//+4a63iln0BCP3bsRQ6luwZGMJDhKltI2LDjvDoVcCehXCPGnAIgIuIAwlP8QqRMSj4XxJanINGKHAfDai8aYLgTk9ZNkmXV1STRDAaLkgUrTdbliNs22wewbbIPb7cJU5gJe6sYVk03UJCLIi/XCvww/3DOkzqp5T6N/YmyDa7eZPMwnkkpUrKyEl76oHe/V3OG4oGY7PMbAfzkLH9drP2kcyD+DV80mdTSB7f52r/qgZFLcsT86NclfIFDPmO9Q=="*)(*]VB*)]}}(*||*)(*1:eJxTTMoPSmNmYGAo5gUSYZmp5S6pyflFiSX5RcEsQBH3osyUNCaQPIgXVJqTWswPFXbKrwguSEzOzEsvTmOEqfDJLC5BVQ82Jii/vDgTSDOgmSWAMMsxJzM9Lzc1rwSvYexAhnN+TmluXjFESWpaCQBkRyz7*)(*]GB*)

Manipulate colors pre-optimization

Hue, RGBColor, and Opacity can now be used inside Animate, Manipulate, or Refresh in 2D graphics without triggering a full reevaluation:

Manipulate[
  Graphics[{Hue[h, l], Disk[]}, ImageSize->Small], 
  {h,0,1,0.2}, {l,0,1,0.2}, ContinuousAction->True]

Just-in-Time transpiler (JIT) will take care of optimizing updates of color/opacity attributes on the graphical canvas.

Better Mini Apps

We improved our mini apps export feature. Now you don't have to use WLX language at all. Even a single input cell can become an app.

Try this in a fresh notebook and then export this single cell as a mini app using %3Csmall%20%3EShare%3C%2Fsmall%3E menu

Manipulate[x, {x,0,1}]
%3Cbr%20%2F%3E %3Chr%20%2F%3E %3Cbr%20%2F%3E

Better autocomplete for Greek letters

We improved UX on typing α\alpha and other common greek letters used in math. Start with %3Ckbd%20%3EESC%3C%2Fkbd%3E key in the input field:

File downloading

We introduced a new file container FrontFileDownload, that allows to pass any binary data from Wolfram Kernel in a form of a file without touching the file system of OS.

For example:


FrontFileDownload["myFile.png"]

or construct it from pure ByteArray:

f = FrontFileDownload[ByteArray[{1,2,3}], "test.bin"];

Then you can initiate a download file dialog by evaluating the result on the frontend:

FrontSubmit[f]

This is helpful for cases where WLJS is running on a server (Docker image), since the usual SystemDialogInput will not make sense there, and any Export function will operate on the server's local file system.

Alternatively it has a button-like output form allowing to embed files into the notebook:

f

It will create a download button for a file.

Fixed bugs

  • Unicode issues in PlotLegends and other elements that rely on the FrontFetch function.
  • Graph objects are not rendered correctly when their size is less than 1 KB.
  • Fixed MCP tool kernel_evaluate and summarization issues on nested associations
  • Rasterize and PDF export stop working after a kernel restart.
  • EmitSound and Speak stop working after a while in the same session.
  • Custom windows cannot be made smaller in the desktop version.