<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Márk Tolmács</title>
    <description>The latest articles on Forem by Márk Tolmács (@marktolmacs).</description>
    <link>https://forem.com/marktolmacs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1653421%2F9aa95fdb-13ac-499f-a5c9-44247be6744f.png</url>
      <title>Forem: Márk Tolmács</title>
      <link>https://forem.com/marktolmacs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/marktolmacs"/>
    <language>en</language>
    <item>
      <title>Building Elbow Arrows in Excalidraw (Part 2)</title>
      <dc:creator>Márk Tolmács</dc:creator>
      <pubDate>Fri, 19 Dec 2025 12:55:29 +0000</pubDate>
      <link>https://forem.com/marktolmacs/building-elbow-arrows-in-excalidraw-part-2-4190</link>
      <guid>https://forem.com/marktolmacs/building-elbow-arrows-in-excalidraw-part-2-4190</guid>
      <description>&lt;h2&gt;
  
  
  Previously on Building Elbow Arrows...
&lt;/h2&gt;

&lt;p&gt;In part one we identified the design goals (shortest route, minimal segments, proper arrow orientation and shape avoidance), but our naive and greedy first approach to the algorithm turned out to be inadequate on multiple fronts. Therefore this approach is abandoned in favor of a different algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Games to the Rescue
&lt;/h2&gt;

&lt;p&gt;The design goals were suspiciously similar to the problem of pathfinding in video games, which is a common, well researched problem. Characters in these games often have to find their way from point A to point B on the game map, without getting blocked by obstacles and use the shortest route. They also need to make it look like a human would take that route, so these characters don't break the immersion. Thus, we turn out focus to A* search algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Foundations
&lt;/h3&gt;

&lt;p&gt;To understand A* pathfinding we need to understand the context, some of the other graph search algorithms it is built and improved on, and how can they help solve our challenge. To begin with, let's talk about where A* comes from. It is part of a family of algorithms for graph search, which all aim to find the shortest path in a graph of nodes connected by edges. While this makes them useful far beyond just games, we'll just focus on pathfinding in a 2D grid (i.e. the canvas). How do we convert the canvas into a graph you ask? If we split it up into a 2D grid (where at its extreme every pixel is its own cell), the neighboring (common) sides of each two cell can be represented by edges of a graph where the nodes such an edge connects are the cells themselves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof061u9d528abbsl1imy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof061u9d528abbsl1imy.png" alt="Nodes to grids" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we can find a route across these node edges (grid cells) satisfying our original constraints, we would just draw the elbow arrow itself!&lt;/p&gt;

&lt;h3&gt;
  
  
  Breadth-First Search
&lt;/h3&gt;

&lt;p&gt;One simple graph search algorithm which finds the guaranteed shortest path in a graph is the humble Breadth-First Search (BFS), which is where we will start our exploration. In essence, BFS tries to visit all the directly connected unvisited nodes from already visited nodes in every iteration (with the start cell being the only visited node at the beginning), until the cell containing the end point is visited. While we do this exploration, we keep a record of which adjacent node we &lt;em&gt;come from&lt;/em&gt; for every node as we visit them, forming a contiguous linked list back toward the start node. Then when we reach the end point cell (we can do an early exit, it will be optimal), we can &lt;em&gt;backtrack&lt;/em&gt; through the linked list of edges all the way to the start. This will give us the shortest path, but it will not look like an elbow arrow and is definitely not visually pleasing. It is however a start we can build on!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: If you're interested in diving deeper into BFS, DFS, and how A* works, I recommend this &lt;a href="https://www.redblobgames.com/pathfinding/a-star/introduction.html" rel="noopener noreferrer"&gt;article, which dives deeper into these algorithms and offers interactive visualizations&lt;/a&gt;. This article will only cover the key insights building toward the final elbow arrow implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Dijkstra's Algorithm
&lt;/h3&gt;

&lt;p&gt;BFS creates the shortest path to our end cell, it also avoids obstacles, but it doesn't look like an elbow arrow path. To have the right arrow shape (i.e. orthogonal bends), we need to find an incentive for our algorithm to take this desired path. This is where Dijkstra's algorithm comes into play. This algorithm operates on weighted graph edges, using these weights as cost to visit a certain neighbor. To illustrate what this means to our 2D canvas grid, imagine that the canvas has a 3rd depth dimension. The cells along all the possible correct elbow arrow routes are flat, while everywhere else there is a steep height difference, creating a "wall". Our "maze generator" can then take into account all remaining design goals and only create these valleys, which satisfy these requirements. Of course this "maze generator" cannot pre-compute the "terrain" because the grid (or graph) is infinite (Excalidraw has an infinite whiteboard canvas), so we need it to tell us at any cell which neighbors are cheap to visit and which are (preferably prohibitively) expensive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distance functions
&lt;/h3&gt;

&lt;p&gt;The answer to our needs is a well chosen distance function of course! This way the edge weights should naturally decrease toward the end cell creating a downward "slope" on our terrain (a negative gradient), while hopefully make incorrect path choices expensive. When we talk distance we often refer to the [Euclidean distance]which is just the length of line segment connecting two endpoints, but there are some clever distance definitions which can help us out in this special maze generation.&lt;/p&gt;

&lt;p&gt;Our desired arrow route happens to be a &lt;a href="https://en.wikipedia.org/wiki/Taxicab_geometry" rel="noopener noreferrer"&gt;Taxicab or Manhattan geometry&lt;/a&gt; and therefore we can use the Manhattan distance calculation to tell at every cell which neighboring cell takes us closer to our goal with it and get (some of) the right shape(s). This is calculated for two cells by first having the familiar &lt;a href="https://en.wikipedia.org/wiki/Cartesian_coordinate_system" rel="noopener noreferrer"&gt;Cartesian coordinates&lt;/a&gt; ssigned to our 2D grid ([x = 12, y = -5] etc) and then adding together the absolute differences of their coordinates along each dimension. For example, if you have one point at [5, 12] and the other point at [10, -3] their Manhattan distance is |5 - 10| + |12 - (-3)| = 5 + 15 = 20. On the illustration below the green line is the Euclidean distance and the red, blue and yellow are equal Manhattan length paths.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yv5emzw56kv1fjstm2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4yv5emzw56kv1fjstm2g.png" alt="Manhattan distance courtesy of Wikipedia" width="300" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional heuristics
&lt;/h3&gt;

&lt;p&gt;Querying the Manhattan distance at every cell we want to visit in our Dijkstra Algorithm give us a metric we can use to generate the "maze" for our search algorithm, but it offers us multiple equally weighted paths and we need to choose the one with the shortest amount of orthogonal bends. So we add an additional term to our "maze generator" to not just consider the Manhattan distance of a neighboring cell and the end cell, but also increase the cost of neighbors if it would be a bend in the path, i.e. our search metric is: Manhattan distance + Bend count. This weighing function paired with Dijkstra's Algorithm now generates correct, visually pleasing elbow arrows if there are no obstacles to avoid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exclusion Zones
&lt;/h3&gt;

&lt;p&gt;In order to support shape avoidance we need to support the deactivation of some cells. These should behave like infinitely high walls in our maze, so the graph search algorithm never visits them. This is easily achieved by adding a flag to every cell and mapping shapes to these cells and disabling those cells the shapes cover. However combined with our modified distance calculation it starts to take sub-optimal paths around shapes. The reason is that we can't see the entire "map" (i.e. the grid), it's like a "fog of war" and we uncover it at every iteration of the search algorithm. Obstacles we should avoid well in advance might not be "seen" until it's too late.&lt;/p&gt;

&lt;p&gt;Of course introducing a backtracking process is one practical way to address this, but that can be computationally unfeasible. To reduce the unnecessary exploration steps and have a way to incorporate our heuristics, the A* algorithm seemed like an optimal choice. The A* algorithm is similar to Dijkstra's Algorithm, it considers the weight of graph edges, but prioritizes exploring those neighboring nodes (or cells), which offer the best chance to take us closer to the end node (cell) and not waste precious computational cycles. It also promises a one-pass solution, which is also memory efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is A*?
&lt;/h3&gt;

&lt;p&gt;The key insight of A* is that it balances two factors for every cell (node):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;g(n)&lt;/strong&gt;: The actual cost to reach a node from the start. This will be our bend count and some additional heuristics later down the road&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;h(n)&lt;/strong&gt;: The estimated cost to reach the goal from that node (the Manhattan distance + grid exclusion + even more heuristic functions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;f(n) = g(n) + h(n)&lt;/strong&gt;: The total estimated cost of the path through that node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The algorithm maintains two sets of nodes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open set&lt;/strong&gt;: Nodes to be evaluated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closed set&lt;/strong&gt;: Nodes already evaluated&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the start node to the open set&lt;/li&gt;
&lt;li&gt;Loop until the open set is empty:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Select the node with the lowest f(n) score&lt;/li&gt;
&lt;li&gt;If it's the goal, reconstruct the path and return&lt;/li&gt;
&lt;li&gt;Move it to the closed set&lt;/li&gt;
&lt;li&gt;For each neighbor:

&lt;ul&gt;
&lt;li&gt;Calculate tentative g score&lt;/li&gt;
&lt;li&gt;If the neighbor is in the closed set and the new g score is worse, skip it&lt;/li&gt;
&lt;li&gt;If the neighbor isn't in the open set or the new g score is better:

&lt;ul&gt;
&lt;li&gt;Update the neighbor's scores&lt;/li&gt;
&lt;li&gt;Set the current node as the neighbor's parent&lt;/li&gt;
&lt;li&gt;Add the neighbor to the open set&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;If the open set becomes empty without finding the goal, no path exists&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By always exploring the node with the lowest f(n) value, A* efficiently finds optimal paths without exhaustively searching every possibility, which exactly what we want. At this stage the implementation mostly worked as expected, but edge cases kept popping up where a human would expect a different routing choice. The other problem was performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Optimizations
&lt;/h2&gt;

&lt;p&gt;While the behavior was close, the performance was really starting to cause problems, especially when multiple elbow arrow paths had to be calculated for every frame. Targeting at least 120Hz smooth rendering with ~50 elbow arrows re-routing every frame was a lofty, but achievable goal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Binary Heap Optimization
&lt;/h3&gt;

&lt;p&gt;The low hanging fruit was obviously the open set of nodes (cells). At every iteration a linear lookup across open nodes is not ideal. The obvious cure is a binary heap data structure with its O(log n) complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-Uniform Grid
&lt;/h3&gt;

&lt;p&gt;Operating on a pixel grid (or one node per pixel when looking at it as a graph) makes a significant amount of unnecessary A* iterations (99% and up, depending on conditions), which is a huge waste. Even having cells with just a few pixels is an unimaginable waste (and also wouldn't be pixel perfect).&lt;/p&gt;

&lt;p&gt;The solution to this problem is to draw a new grid, which only has nodes on a grid where a potential bend could be. Turns out this is easier than one would think, since aesthetically pleasing elbow arrows always break at shape corners, start and end point headings, and halfway between shapes to be avoided - with optional padding around shapes accounted for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqb0ivwhzcer76sht9r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqb0ivwhzcer76sht9r5.png" alt="Non-uniform grid sources and placement" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the algorithm operates on graph nodes, nothing forces us to make this a uniformly-sized grid (i.e. for the cells to be exactly the same size). The only thing that matters to A* and the Manhattan distance calculation is neighboring cells and contiguous Cartesian coordinates. Lucky us!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adapting A* with Manhattan distances for Aesthetic and Performant Elbow Arrows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Aesthetic Heuristics
&lt;/h3&gt;

&lt;p&gt;While the basic A* implementation produced better results than the BFS approach, as mentioned previously, there were some cases beyond the excessive bend count that generated unnatural routes. With carefully constructing additional heuristics, we were able to address these, albeit changes to the A* metric unilaterally affect the behavior in all edge cases, so testing and fine-tuning happened manually to arrive at the final solution.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Backward Visit Prevention
&lt;/h4&gt;

&lt;p&gt;Arrow segments are prohibited from moving "backwards," overlapping with previous segments when there is no route other than backwards. This not just prevents spike-like paths (directly reversed segments), but almost completely avoids routes where a U-like arrow configuration would be required to avoid a shape.&lt;/p&gt;

&lt;p&gt;This is achieved by tracking the direction of visits and looking back one step and forward one step to recognize the problematic configuration. Together with the very specific grid selection, this prevents the edge cases where this issue would trigger.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: This only works because there can be at most 2 closed axis-aligned bounding boxes (the shapes) to avoid. With more obstacles in between would certainly re-surface this issue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  2. Segment Length Consideration
&lt;/h4&gt;

&lt;p&gt;Longer (Euclidean distance-wise) straight segments are preferred over multiple short segments, even though previously the Manhattan distance was selected as our main metric driving the A* pathfinding. This is because - again - in some edge cases the non-uniform grid would assume that what it generated was the shortest path, but when projected onto the pixel grid it clearly created short step patterns where it shouldn't. It was especially prevalent with connected shapes being distant from each other.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Shape Side Awareness
&lt;/h4&gt;

&lt;p&gt;When choosing between routing left or right around an obstacle, the algorithm considers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The length of the obstacle's sides&lt;/li&gt;
&lt;li&gt;The relative position of start and end points heading-wise (see heading in part 1 of this series)&lt;/li&gt;
&lt;li&gt;The parallel or orthogonal relation of the arrow and the shape's bounding box side&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Short Arrow Handling
&lt;/h4&gt;

&lt;p&gt;Special logic for when the start and end points are very, very close, which prevents excessive meandering and loops. In one case where connected shapes almost overlap and the start and end points are very close, there is a hard-coded simplified direct route because the expected route would never emerge from the algorithm.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Overlap Management
&lt;/h4&gt;

&lt;p&gt;The implementation also explicitly detects when shapes cover the start and/or end points. When connected shapes or points overlap, it selectively disables avoidance for one shape or both so there is a valid route possible, even if the exclusion zones would prevent it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other solutions
&lt;/h2&gt;

&lt;p&gt;Using A* with Manhattan distances is not the only way to have orthogonal (elbow) arrows, so I'd like to point you to existing research on this topic I was considering using, but for various reasons these remained routes not taken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/pdf/1612.05064" rel="noopener noreferrer"&gt;Gladisch, V. Weigandt, H. Schumann, C. Tominski: Orthogonal Edge Routing for the EditLens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://link.springer.com/content/pdf/10.1007/978-3-642-11805-0_22.pdf" rel="noopener noreferrer"&gt;Michael Wybrow, Kim Marriott, and Peter J. Stuckey: Orthogonal Connector Routing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;What started as a simple feature request - "add elbow arrows" - evolved into a sophisticated pathfinding challenge. By combining classical algorithms (A*), domain-specific optimizations (non-uniform grids, only two shapes to avoid etc.), and carefully tuned heuristics (aesthetic weights), Excalidraw's elbow arrows speed up diagramming significantly and avoid diagrams previously hand-drawn and hand-managed.&lt;/p&gt;

&lt;p&gt;It even made possible some other powerful features &lt;a href="https://www.youtube.com/watch?v=TOd0-DWqUTE" rel="noopener noreferrer"&gt;like keyboard shortcut flowchart creation&lt;/a&gt; which further empower Excalidraw users with serious diagramming needs.&lt;/p&gt;

&lt;p&gt;Come join me for the 3rd and final part of this series on elbow arrows, where I walk you through how controlling and fixing arrow segments makes difficult-to-predict arrow routes possible for power users!&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>excalidraw</category>
    </item>
    <item>
      <title>Building Elbow Arrows in Excalidraw (Part 1)</title>
      <dc:creator>Márk Tolmács</dc:creator>
      <pubDate>Thu, 09 Oct 2025 09:24:29 +0000</pubDate>
      <link>https://forem.com/marktolmacs/building-elbow-arrows-in-excalidraw-part-1-4h9e</link>
      <guid>https://forem.com/marktolmacs/building-elbow-arrows-in-excalidraw-part-1-4h9e</guid>
      <description>&lt;p&gt;As you may know, Excalidraw is an online whiteboarding application that stands out from the crowd with its distinctive hand-drawn, sketchy aesthetic. Despite this (or likely for this very reason) it is loved and embraced by professionals in various verticals including IT, data analysis, engineering, sciences and much more. Their work often includes &lt;a href="https://plus.excalidraw.com/use-cases/flowchart" rel="noopener noreferrer"&gt;creating diagrams conveying flows of information or processes&lt;/a&gt;, where clarity is paramount. One of the tools they use to indicate connection between concepts or states is arrows, but straight arrows on a busy board can get clunky fast. Therefore a new type of diagramming arrow was needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case for Elbow Arrows
&lt;/h2&gt;

&lt;p&gt;Enter elbow (or orthogonal) arrows. These arrows follow 90-degree angles, creating clean, professional-looking diagrams that are easy to follow and aesthetically pleasing. Excalidraw users with heavy diagramming workflows already emulated this type of arrow by painstakingly adding points to simple arrows and dragging them into this 90-degree configuration. Therefore it was clear that implementing an arrow type, which supports this arrow routing would bring instant value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fexcalidraw.nyc3.cdn.digitaloceanspaces.com%2Flp-cms%2Fmedia%2FProcess_Flowchart_Example_in_Excalidraw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fexcalidraw.nyc3.cdn.digitaloceanspaces.com%2Flp-cms%2Fmedia%2FProcess_Flowchart_Example_in_Excalidraw.png" alt="Create Flowcharts in Excalidraw" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also quickly realized that it would only be accepted if it "guesses" correctly how a human would route the arrow. This turned out to be the biggest challenge of all. If the arrows look "weird" nobody would use them. So we had to get it right, no matter what "right" means in this context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Goals
&lt;/h2&gt;

&lt;p&gt;We knew from previous experience that we'll definitely need to add additional constraints for passing the "as a human would do" mark. We can't just draw &lt;em&gt;an&lt;/em&gt; elbow arrow, it has to go the &lt;em&gt;right way&lt;/em&gt;. While it was clear that this will be an iterative project, we were already able to jot down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shortest route&lt;/strong&gt; - the arrow should take the most direct path from start to end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal segments&lt;/strong&gt; - fewer turns mean cleaner, more readable diagrams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proper arrow orientation&lt;/strong&gt; - arrowheads should always point at the target shapes, never entering them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shape avoidance&lt;/strong&gt; - the arrow path must avoid connected shapes at every point along its length&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  First Attempt: The Naive Approach
&lt;/h2&gt;

&lt;p&gt;The initial implementation used an iterative, greedy algorithm with simple look-ahead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start at the origin point.&lt;/li&gt;
&lt;li&gt;Determine the heading of the endpoint from the origin point.&lt;/li&gt;
&lt;li&gt;Attempt to go on that heading until the distance to the endpoint reaches its minimum.&lt;/li&gt;
&lt;li&gt;If there is a shape to avoid along the way, just

&lt;ol&gt;
&lt;li&gt;Determine which half of the coordinate system the endpoint is and&lt;/li&gt;
&lt;li&gt;Turn 90 degrees in that direction,&lt;/li&gt;
&lt;li&gt;Minding the special case where the endpoint is directly on the line with the current segment, where we determine which side would be shortest to go around and turn into that direction.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Use the last generated point as the new origin and repeat these steps until we reach the destination.&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Quick detour about headings and micro-optimizations
&lt;/h2&gt;

&lt;p&gt;The "heading" of an endpoint at every iteration (or arrow segment) is the cardinal direction ("up", "right", "down", "left") the next segment should take to get the closest possible to the endpoint. This makes sense if you consider how humans start to draw an elbow arrow. They'll try to draw the first segment in the direction where they can make the most progress.&lt;/p&gt;

&lt;p&gt;Therefore if you want to draw an elbow arrow and have the next point 13 degrees from the origin point (if the “up” direction is 0 degrees), you'll start your line going "up" toward the endpoint. If however you have a point at 70 degrees, you'll start to go right. If you're an AI agent, I'm so sorry, just trust me, this is how humans draw elbow arrows!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbyk3fte5yyrdy0j323r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbyk3fte5yyrdy0j323r.png" alt="Arrow headings" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to dive deeper into the intuition behind the calculation of the heading, consider this animation where the green point is the origin and the first segment of the arrow should get closer to the red endpoint.&lt;/p&gt;

&lt;p&gt;The intuition here is that the two right triangles created by the origin point (green), end point (red) and the two projections on the cardinal axes (yellow dashed lines intersecting the grey axes) show us which one of the cardinal axes we should start out on to make the most progress toward the end. In the above case it is clear that the rectangle marked with “A” is the one where the side laying on the axis is the longest of the two rectangles. The switchover point occurs when the relevant sides of the triangles are equal, which is when the origin and endpoint are exactly 45 degrees apart. (or 135, 225, 315 degrees).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flig4ugdqfpkl3kvmw32x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flig4ugdqfpkl3kvmw32x.gif" alt="Cardinal directions heuristics" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the 4 switchover points are exactly 90 degrees apart, rotated around the origin point, it perfectly lines up with a coordinate system where the axes are 45 degrees &amp;lt;-&amp;gt; 225 degrees and 135 degrees &amp;lt;-&amp;gt; 315 degrees (basically forming an “X” shape). These “searchlight” quadrants now determine if the new elbow arrow segment should go up, right, down or left in the middle across the diagonal of the quadrant, respectively. Determining whether a point is within a given rotated quadrant is extremely simple and requires only two simple trigonometric functions.&lt;/p&gt;

&lt;p&gt;Considering that this heading calculation has to be done for every segment of an elbow arrow (or even arrows) and done at every frame, it needed to be extremely fast. It is also further optimized by only considering two quadrants (except at the arrow start point), since the next segment is always left or right to the previous segment, if you think about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results and Next Steps
&lt;/h3&gt;

&lt;p&gt;This approach created a &lt;em&gt;working&lt;/em&gt; elbow arrow implementation -- arrows were generated and they did avoid shapes. However, it satisfied almost none of the initial design goals. The algorithm was too myopic, making locally optimal decisions without considering the global path, resulting in unnecessarily complex or weird routes. Here's one of the failed examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99pobjt7v799qg06pgo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99pobjt7v799qg06pgo8.png" alt="The naive approach" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The green dotted arrow is the final elbow arrow implementation and the black path is the naive implementation. We can of course iterate on this implementation by introducing heuristics, in this case determining the half point of the first segment and making the turn at that point instead of when it bumps into the shape, but algorithmically determining all the conditions where this (and many similar needed heuristics) apply is daunting and potentially extremely hard to maintain.&lt;/p&gt;

&lt;p&gt;Clearly, a new approach was needed. Come back for the next part where we tackle this and all other problems with an algorithm we borrowed from video game development!&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A Gentle Introduction to WebAssembly in Rust (2025 Edition)</title>
      <dc:creator>Márk Tolmács</dc:creator>
      <pubDate>Tue, 14 Jan 2025 19:48:34 +0000</pubDate>
      <link>https://forem.com/marktolmacs/a-gentle-introduction-to-webassembly-in-rust-2025-edition-1iac</link>
      <guid>https://forem.com/marktolmacs/a-gentle-introduction-to-webassembly-in-rust-2025-edition-1iac</guid>
      <description>&lt;p&gt;&lt;em&gt;Updated: 2025-01-17 Formatting fixes, fix &lt;code&gt;web-sys&lt;/code&gt; features by adding 'console'&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Updated: 2025-01-17 Video added&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you rather watch a video, a pair-programming session can be found below walking through this article.&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=kdHfOhBdv1E" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo98y2kdglbqwe3tutoi9.jpg" alt="An introduction to WASM in Rust with Márk Tolmács" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;It’s clear &lt;a href="https://webassembly.org/" rel="noopener noreferrer"&gt;WebAssembly&lt;/a&gt; is one of the more popular up-and-coming technologies out there. Its promise, a universal executable format, is not new. In fact it dates back to 1995 (almost thirty years ago!) with Java. Arguably, Java was successful in some areas, many enterprise software is built on Java after all, it tried for a brief time (Java Web Start) and eventually failed to ride the stellar rise of the world wide web. Microsoft .NET is a younger contender, but it arguably suffering from the same adoption challenge as Java. While it can run on most systems now, the web is still not one of them.&lt;/p&gt;

&lt;p&gt;Enter WebAssembly (or WASM for short), supported by a wide consortium of players, developed in the open and as an open standard, with the WEB as its primary platform. While it’s too early to tell if WebAssembly will be the winner we’ve been waiting for, its adoption is wide enough, the core technology is stable enough that it’s worth considering it for even professional cases. If in doubt, just consider that &lt;a href="https://www.figma.com" rel="noopener noreferrer"&gt;Figma&lt;/a&gt;, the interface design software, is &lt;a href="https://www.figma.com/blog/webassembly-cut-figmas-load-time-by-3x/" rel="noopener noreferrer"&gt;built on C++ and WebAssembly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why is a portable, widely supported executable format is such a big deal, you ask? One of the main reasons is that there are a LOT of software already written and most of them are complex systems, not easily ported to other languages and tooling. 99% of the time these software is written in C or C++. WebAssembly offers direct compilation from C, C++ and many more languages an environment (including Rust!) without major hiccups. And that is a previously unseen capital Bid Deal! Besides making software porting almost trivial it’s also a nice benefit that it can often run compute intensive tasks faster than JavaScript.&lt;/p&gt;

&lt;p&gt;So in this guide we’ll walk through setting up the tooling and development environment for building and using WebAssembly in Rust, embedding it in a TypeScript project, review how communication between TypeScript and Rust can happen, then finally how you can debug your WebAssembly directly in the browser and/or your favorite IDE. I will use Visual Studio Code as the IDE and Chrome as the browser, but apart from some debugging options, you can reproduce these in your tool of choice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: This guide heavily relies on the excellent&lt;/em&gt; &lt;a href="https://rustwasm.github.io/docs/book" rel="noopener noreferrer"&gt;&lt;em&gt;Rust WASM Book&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, which contain a lot more examples and details than this article. I recommend checking it out after finishing this one.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What exactly is WebAssembly?
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, WebAssembly is an open standard of a binary 32 bit instruction set architecture (&lt;a href="https://en.wikipedia.org/wiki/Instruction_set_architecture" rel="noopener noreferrer"&gt;ISA&lt;/a&gt;) for a stack-based, sandboxed virtual machine. That’s a heavy load of &lt;a href="https://www.oxfordreference.com/display/10.1093/acref/9780195369380.001.0001/acref-9780195369380-e-1999" rel="noopener noreferrer"&gt;&lt;em&gt;terminus technicus&lt;/em&gt;&lt;/a&gt;, but it’s an apt summary.&lt;/p&gt;

&lt;p&gt;In plain English, it means that,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlike JavaScript, its final form is binary, not text, similar to how programs on your machine are in binary format.&lt;/li&gt;
&lt;li&gt;It is a virtual machine by virtue of not running directly on your hardware as its binary code is translated on-the-fly when you run it, offering the portability you’d expect from a web technology.&lt;/li&gt;
&lt;li&gt;Sandboxed, so any interaction with the outside world is carefully scrutinized and approved by the end user, which offers safety guarantees for users that when they load a WebAssembly module on a web page, it cannot access their data or modify their system, unless explicit permission is given.&lt;/li&gt;
&lt;li&gt;It’s 32 bit, meaning that we can allocate a maximum of 4Gb of RAM for our WebAssembly application (until &lt;a href="https://github.com/WebAssembly/memory64" rel="noopener noreferrer"&gt;WASM64&lt;/a&gt; comes around).&lt;/li&gt;
&lt;li&gt;Stack-based is… you know what?! Let this be a concern for compiler developers and let’s get to coding!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting Up Tooling
&lt;/h2&gt;

&lt;p&gt;The reference implementations and the most mature WebAssembly development pipeline called &lt;a href="https://github.com/WebAssembly/binaryen" rel="noopener noreferrer"&gt;Bynarien&lt;/a&gt; is still built around C/C++, mainly because the amount of useful code people want to run in the browser was built with C/C++. The Rust community is building it’s own WebAssembly pipeline, however it’s in a state of Tier 2 without Host Tooling at the beginning of 2025. This means that while it is easily and safely used by developers even for production purposes, it lacks some native tooling. This is where we will rely on the Bynarien toolbox to patch in the holes where the Rust WASM pipeline is lacking.&lt;/p&gt;

&lt;p&gt;Let’s install the required tools and set up the project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install yarn (or npm, or pnpm, if you don’t have it already)
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; corepack &lt;span class="nb"&gt;enable&lt;/span&gt;    &lt;span class="c"&gt;# I'll use corepack because I have node@20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Start a new TypeScript project called ‘wasm-on-web’ with &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; (or your framework of choice, if any)
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn create vite wasm-on-web &lt;span class="nt"&gt;--template&lt;/span&gt; vanilla-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Commit the current state to Git because we will overwrite some files and you’ll need the old ones
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git init   &lt;span class="c"&gt;# If you haven't initialized the git repo yet&lt;/span&gt;
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial setup"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install Rustup to manage your Rust installation and toolchains
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Set the Rust channel to stable
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustup default stable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Download the compilation toolchain for WASM
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustup target add wasm32-unknown-unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install some tools we’ll use during our exercise
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-tools
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-opt
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo &lt;span class="nb"&gt;install &lt;/span&gt;wasm-pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install the Bynarien toolkit for our investigations. I use brew, but on Windows you might need to &lt;a href="https://github.com/WebAssembly/wabt?tab=readme-ov-file#building-windows" rel="noopener noreferrer"&gt;build it yourself&lt;/a&gt; or use a package manager like Scoop and use the &lt;a href="https://scoop.sh/#/apps?q=wabt" rel="noopener noreferrer"&gt;pre-built package from extras&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;wabt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;cargo-generate&lt;/code&gt; to quickly scaffold our Rust project over the Vite project we created in step 2
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo binstall cargo-generate   &lt;span class="c"&gt;# Install pre-built binary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: cargo-generate needs libssl-dev (openssl) installed if you use &lt;code&gt;cargo install cargo-generate&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overlay the Rust project of our TypeScript / Vite project
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
&amp;gt; cd wasm-on-web
&amp;gt; yarn install
&amp;gt; cargo generate              \
        --init                \
        --name wasm-on-web    \
        --overwrite           \
        --git https://github.com/rustwasm/wasm-pack-template
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Note: `cargo-generate` needs to overwrite some files, because it conflicts with Vite, but the only thing you need to merge is `.gitignore`. You’ll need both the original lines and the newly added ones.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Test if everything works so far&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn dev  &lt;span class="c"&gt;# Should open a web browser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With &lt;code&gt;Ctrl + C&lt;/code&gt; you can exit the Vite server. You can also commit it into Git now.&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sidenote: Publish your Rust WASM package on &lt;a href="http://npmjs.com" rel="noopener noreferrer"&gt;npmjs.com&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If your WASM code is self contained in Rust, you can build it in production mode and publish it on &lt;a href="http://npmjs.com" rel="noopener noreferrer"&gt;npmjs.com&lt;/a&gt; right now. The &lt;code&gt;wasm-pack&lt;/code&gt; tool creates all the TypeScript types, package.json skeleton and anything else needed for a complete package. It is recommended that you review and update your &lt;code&gt;package.json&lt;/code&gt; file prior to publishing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; wasm-pack build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build and Integrate the WebAssembly Module
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;We need to build the WASM module so we can import it in the TypeScript project
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; wasm-pack build &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the WASM package to our TypeScript project
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add &lt;span class="nb"&gt;link&lt;/span&gt;:./pkg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: It is important to add our WASM package as ‘link’, otherwise when we rebuild the WASM module Vite will not pick up the new version!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the Vite plugins required to interoperate with WASM
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       vite-plugin-wasm &lt;span class="se"&gt;\&lt;/span&gt;
       vite-plugin-top-level-await &lt;span class="se"&gt;\&lt;/span&gt;
       vite-plugin-wasm-pack-watcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Configure the WASM loader in Vite by creating &lt;code&gt;vite.config.dev.ts&lt;/code&gt; and adding the following contents:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;topLevelAwait&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-top-level-await&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasmPackWatchPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-wasm-pack-watcher&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/**/*.rs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;wasmPackWatchPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;topLevelAwait&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the production Vite configuration file &lt;code&gt;vite.config.ts&lt;/code&gt; and add the following contents:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;topLevelAwait&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vite-plugin-top-level-await&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;topLevelAwait&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: We can’t use the same config file because of the watch configuration, it would hang the &lt;code&gt;vite build&lt;/code&gt; command when we build for production. 6. Add the &lt;code&gt;npm-run-all&lt;/code&gt; package as a dev dependency in preparation for the next step below:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Add the &lt;code&gt;npm-run-all&lt;/code&gt; package to our TypeScript project to help with running the project
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;     &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; npm-run-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: This is a platform-independent way to run npm scripts one after the other with the &lt;code&gt;run-s&lt;/code&gt; shortcut, which makes it possible for the Windows folks to follow this tutorial without issues. Even if you don’t use Windows it’s polite to have a solution in place which works for all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Modify “&lt;em&gt;scripts&lt;/em&gt;” section in &lt;code&gt;package.json&lt;/code&gt; to call &lt;code&gt;wasm-pack&lt;/code&gt; before starting Vite for all configurations, so a fresh WASM build is always ready for us at the start dev start and the final WASM module is build in release mode before vite production build happens:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s wasm-pack:release tsc vite:build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s wasm-pack:dev vite:dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"vite:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"vite:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite -c vite.config.dev.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"vite:preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite preview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"wasm-pack:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wasm-pack build --dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"wasm-pack:release"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wasm-pack build"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start the Vite dev mode and continue with writing our Rust WASM code
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exporting and Importing in Rust
&lt;/h2&gt;

&lt;p&gt;Opening up &lt;code&gt;src/lib.rs&lt;/code&gt; we can see that we have a &lt;code&gt;greet()&lt;/code&gt; function already exported and ready for us to be called. We know it’s exported because it has the &lt;code&gt;[#wasm_bindgen]&lt;/code&gt; macro applied to it from the wasm-bindgen package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, wasm-on-web!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: You can in fact add the &lt;code&gt;#[wasm_bindgen]&lt;/code&gt; macro to enums, structs and impls, not just stand-alone fns!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s another trick in this file above the &lt;code&gt;greet()&lt;/code&gt; function, which you’ll use when you want to call JavaScript functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specifices an &lt;code&gt;extern&lt;/code&gt; block which contain external (JS) functions we want to call from our Rust WASM module. These can be called with the C calling convention, not the Rust calling convention, hence &lt;code&gt;extern “C”&lt;/code&gt;. These should be wired up between JS and Rust too, so you need to add the &lt;code&gt;#[wasm_bindgen]&lt;/code&gt; macro here as well. Not unsurprisingly, this is &lt;strong&gt;unsafe&lt;/strong&gt; and since the Rust compiler won’t be able to verify whether the external function exists, it’s calling signature (parameters) are properly typed, present and ordered the right way. Specifying these incorrectly very likely will crash your program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing From and Exporting To WASM in TypeScript
&lt;/h2&gt;

&lt;p&gt;Now it’s time to open &lt;code&gt;src/main.ts&lt;/code&gt; and import our new Rust WASM module at the top of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wasm-on-web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then on the bottom, just simply call the &lt;code&gt;greet()&lt;/code&gt; method we just imported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Call the greet function from WASM with the&lt;/span&gt;
&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the translation, loading the WASM module and configuration is being taken care of by &lt;code&gt;wasm-bindgen&lt;/code&gt;, the Rust package our template installed. Even TypeScript type definitions are generated for us.&lt;/p&gt;

&lt;p&gt;If everything went well and &lt;code&gt;yarn dev&lt;/code&gt; is running, the browser is open, we’ll see the alert right from the Rust code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4v7zltbovh1h0kbtrw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4v7zltbovh1h0kbtrw7.png" width="571" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to call your JavaScript functions in Rust, you already saw how it is done with the &lt;code&gt;alert()&lt;/code&gt; JS function. One additional step is required, namely that you have to add your function to the global &lt;code&gt;window&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Just to resolve TypeScript errors&lt;/span&gt;
&lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;jsFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsFunction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from JS!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you’re ready to declare and call it in Rust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;jsFunction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;jsFunction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passing Primitive Parameters Back and Forth
&lt;/h2&gt;

&lt;p&gt;A function call seldom worth much without passing data to it in the form of parameters. &lt;code&gt;wasm-bindgen&lt;/code&gt; takes care of this too in simple cases and almost completely in heap allocated types. For example, if you want to pass a custom name to greet to our greet function implemented in Rust, you could do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello {}!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the TypeScript side, just simply pass the parameter and we’re ready to go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this is TS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should show the alert dialog with our new parameter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpfm36dzraee1kjx3hzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpfm36dzraee1kjx3hzi.png" width="560" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the other direction, we’re already seen with the native &lt;code&gt;alert(…)&lt;/code&gt; function, just pass the string slice to the JS function and &lt;code&gt;wasm-bindgen&lt;/code&gt; takes care of it.&lt;/p&gt;

&lt;p&gt;So what happens when you need to use complex types, maybe heap allocated types as parameters? That’s what we’re dealing with next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using existing JavaScript APIs and Functions
&lt;/h2&gt;

&lt;p&gt;I don’t know about you but I’ve had just about enough of the alert dialog and would like to use &lt;code&gt;console.log(…)&lt;/code&gt; and similar native JS APIs. We could wire them up manually, figure out the complex parameter definition, but there is an easier way: The &lt;code&gt;web_sys&lt;/code&gt; package. Let’s install it! Add this to your &lt;code&gt;Cargo.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies.web-sys]&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;
&lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"Window"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"console"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can get rid of our manually implemented &lt;code&gt;alert()&lt;/code&gt; mapping and use the &lt;code&gt;web_sys&lt;/code&gt; &lt;code&gt;console.log&lt;/code&gt; implementation going forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;web_sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;console&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;log_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello {}!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passing Complex Parameters Back and Forth
&lt;/h2&gt;

&lt;p&gt;With complex types we need to consider the fact that what’s a complex type in one language might not be so in the other. A typical example of this is String. String is an owned, complex type in Rust and behaves like a primitive in JavaScript. &lt;code&gt;wasm-bind&lt;/code&gt; hides this difference by making a copy in WASM memory of your JS string when you expect a &lt;code&gt;String&lt;/code&gt; parameter type in Rust. You can make it mutable, but it will only modify the Rust copy of the &lt;code&gt;String&lt;/code&gt;, it will not propagate back to the JS string. In order to have two-way communication, we have to put in some legwork.&lt;/p&gt;

&lt;p&gt;If you have a JS class or object which you’d like to receive as a parameter on the Rust side, you’ll have to define the mapping as an &lt;code&gt;extern “C”&lt;/code&gt; block. Let’s say we have a JS class defined in TS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TSDef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Rust side if we want to access the &lt;code&gt;id&lt;/code&gt; property and the &lt;code&gt;run&lt;/code&gt; method, we have to map it and use it the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;TSDef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Uses the JS "get()" method which is&lt;/span&gt;
    &lt;span class="c1"&gt;// provided by the "class" base prototype chain&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(method,&lt;/span&gt; &lt;span class="nd"&gt;getter)]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TSDef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Uses the JS "set()" method which is&lt;/span&gt;
    &lt;span class="c1"&gt;// provided by the "class" base prototype chain&lt;/span&gt;
    &lt;span class="c1"&gt;// NOTE: "set_&amp;lt;property&amp;gt;" naming is important!&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(method,&lt;/span&gt; &lt;span class="nd"&gt;setter)]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TSDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;#[wasm_bindgen(method)]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TSDef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remote_instance_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsdef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TSDef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Display the id of the instance&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsdef&lt;/span&gt;&lt;span class="nf"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Modify the id on the JS instance&lt;/span&gt;
    &lt;span class="n"&gt;tsdef&lt;/span&gt;&lt;span class="nf"&gt;.set_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"zyxw"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Call a method on the JS instance&lt;/span&gt;
    &lt;span class="n"&gt;tsdef&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you call the &lt;code&gt;remote_instance_param()&lt;/code&gt; function from TypeScript, you’ll see an alert with the original “abcd” message from JS and a console message with “zywx” from the &lt;code&gt;run()&lt;/code&gt; method invoked from Rust reading the modified &lt;code&gt;id&lt;/code&gt; value and printing it on the console.&lt;/p&gt;

&lt;p&gt;What if we want to expose a Rust type to JS? A similar mapping needs to take place. Let’s have a &lt;code&gt;Person&lt;/code&gt; struct which we would like to use in JS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// String is not Copy, so we cannot make it public!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Implement a constructor and a getter/setter for the String field&lt;/span&gt;
&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(constructor)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Getter which automatically gets called on the JS side&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(getter)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Setter which automatically gets called on the JS side&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(setter)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the TS/JS side, we can then simply import it and use it with one big caveat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wasm-on-web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a Person object which is shared between WASM and JS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2343&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Automatically call the setter on the Rust side&lt;/span&gt;
&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jane&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Call the greet function from WASM with the&lt;/span&gt;
&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Don't forget to free the WASM memory when you're done with the shared object!&lt;/span&gt;
&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the &lt;code&gt;Person&lt;/code&gt; type is a WASM type defined in Rust, we need to take care of the de-allocation ourselves when we no longer need it. Unfortunately there is no &lt;code&gt;Drop&lt;/code&gt; mechanic and the memory of JS-allocated Rust native types need to be freed manually!&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging WebAssembly
&lt;/h2&gt;

&lt;p&gt;Now that we have seen how we can transfer data between the two sides, we also became aware of how fragile is the whole setup. Having a robust debugging workflow is essential to quickly identify and root out the inevitable issues.&lt;/p&gt;

&lt;p&gt;To date the best option to do so is via a Google Chrome extension, &lt;a href="https://chromewebstore.google.com/detail/cc++-devtools-support-dwa/pdcpmagijalfljmkmjngeonclgbbannb" rel="noopener noreferrer"&gt;C/C++ DevTools Support (DWARF)&lt;/a&gt;, built by Google’s Engineers. DWARF is one of the debugging information formats for binary code and it’s the type of debug info Rust generates by default, so it’s ideal for our debugging needs. Unfortunately this is Chrome only, so you’ll have to stick with Chrome.&lt;/p&gt;

&lt;p&gt;Once you added this extension to your Chrome, restart the browser so it loads the extension proper. Next you’ll need to enable a setting in the DevTools settings panel (see the cog icon on your DevTools panel after you opened it). It’s called “&lt;em&gt;Allow DevTools to load resources, such as source maps, from remote file paths. Disabled by default for security reasons.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9q5cz2khxc0yyb93hlbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9q5cz2khxc0yyb93hlbp.png" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the browser is ready, we need to set up the Rust side. First, make sure you have the Rust source code installed, because the stack traces most definitely will go into the Rust standard library and having a nice source view into those files will help us figuring out what’s going on much easier than without it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustup component add rust-src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to modify our &lt;code&gt;Cargo.toml&lt;/code&gt; because by default &lt;code&gt;wasm-pack&lt;/code&gt; strips out the DWARF debug information. Add this to the end of &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package.metadata.wasm-pack.profile.dev.wasm-bindgen]&lt;/span&gt;
&lt;span class="c"&gt;# Keep the DWARF debug info for debugging in dev mode&lt;/span&gt;
&lt;span class="py"&gt;dwarf-debug-info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: You may need to clear your browser cache in order for this to work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now if you start your Vite project with &lt;code&gt;vite dev&lt;/code&gt; and load it up in Chrome, then check out the DevTools console, you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gtcxg1wb9ta0e6ykugr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gtcxg1wb9ta0e6ykugr.png" width="757" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a good sign that the Chrome extension found the debug information and loaded it into the DevTools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you can’t see this message and can’t debug your WASM, then check the generated *.wasm files for DWARF debug info. Install &lt;code&gt;wasm-objdump&lt;/code&gt; from HomeBrew and see if you can find “.debug_str”, “.debug_line” and similar custom sections in your WASM. If not, then the DWARF debug info is missing, therefore the extension has nothing to work with.&lt;/p&gt;

&lt;p&gt;You can also run your project with &lt;code&gt;RUST_LOG=info yarn dev&lt;/code&gt; to see if the &lt;code&gt;wasm-pack&lt;/code&gt; step runs &lt;code&gt;cargo build&lt;/code&gt; with the &lt;code&gt;—keep-debug&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Finally if you run &lt;code&gt;yarn dev —debug&lt;/code&gt; you can see what files the browser requests from Vite and if you see it requesting &lt;code&gt;*.dwg&lt;/code&gt; file(s), then the browser extension is working properly, your WASM file should be the problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you open up your sources tab in DevTools, you’ll see that there is a &lt;code&gt;file:///&lt;/code&gt; major section previously missing. Opening it you’ll find your project directory, in it you’ll see the Rust source file, &lt;code&gt;lib.rs&lt;/code&gt; loaded and available. You can now set breakpoints in it and debug like you would a JS file. You can also see the variable values, which might not be immediately useful, but often enough to figure out what’s going on.&lt;/p&gt;

&lt;p&gt;One other benefit this extension brings us is if there is a panic in our code, we will see the stack trace in console with rust file names, line numbers and character positions! Certainly helpful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1pdygmd7u7ybvy4b1ww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1pdygmd7u7ybvy4b1ww.png" width="795" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you see an error message on the Sources tab that refers to files cannot be open, specifically when it starts with &lt;code&gt;file:///rustc/&amp;lt;hash&amp;gt;&lt;/code&gt; then you need to set up a mapping to your rust sources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In case you need to map some directories from the WASM debug informatin, maybe because you built it on a remote machine and your files are in a different folder, there is a way to do it in the Chrome extension settings. Open it up and add the directory mapping from the &lt;code&gt;/rustc/&amp;lt;hash&amp;gt;/&lt;/code&gt; to the absolute path of your Rust sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpb964rd6lup7xxlmyft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpb964rd6lup7xxlmyft.png" width="792" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My mapping was &lt;code&gt;/rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf&lt;/code&gt; =&amp;gt; &lt;code&gt;/home/mtolmacs/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust&lt;/code&gt;. Yours might be different.&lt;/p&gt;

&lt;p&gt;In case you need to figure out the Rust toolchain directory on your system, you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustc &lt;span class="nt"&gt;--print&lt;/span&gt; sysroot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sidenote: WebAssembly on the server-side
&lt;/h2&gt;

&lt;p&gt;While eminently useful for bringing large-scale applications like Figma to the web, it’s gaining popularity on the server-side as well. Highly distributed apps use it as a platform agnostic edge runtime and the Web3 community found use for it as an open platform for smart contract runtimes on the blockchain. One other interesting use-case is &lt;a href="https://swc.rs/docs/plugin/publishing" rel="noopener noreferrer"&gt;platform-independent plugins&lt;/a&gt; for tools like the SWC JavaScript / TypeScript transpiler created by Vercel.&lt;/p&gt;

&lt;p&gt;The solution all these projects use is called WASI, the WebAssembly System Interface, which is a true standard library to access all system resources, just like you would with Rust or C. However WASI is quite young and still not a stable standard. &lt;a href="https://nodejs.org/api/wasi.html" rel="noopener noreferrer"&gt;NodeJS just started to support running WebAssembly with WASI in node@23&lt;/a&gt;, so it is quite experimental yet. Other runtimes like &lt;code&gt;wasmtime&lt;/code&gt; can run the WASI Preview 1 and is reasonably stable.&lt;/p&gt;

&lt;p&gt;So as a final act, let’s download and build a sample WASM app using WASI to open stdio handlers and uses SWC to transpile the input, the output the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git clone https://github.com/zebp/wasi-example-swc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to add the Rust WASI runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rustup target add wasm32-wasip1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to compile it to WASM WASI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cargo build &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasip1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if NodeJS is at least 23.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally create the loader JavaScript which loads the WASM (change the &lt;code&gt;preopens&lt;/code&gt; mapping to avoid any errors)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WASI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:wasi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wasi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WASI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preview1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;preopens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/Users/mtolmacs/Projects/wasi-example-swc/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wasm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../target/wasm32-wasip1/debug/swc-wasi.wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wasi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getImportObject&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nx"&gt;wasi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;examples/async-generator.js | node loader.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to use &lt;code&gt;wasmtime&lt;/code&gt;, just install it and run the WASI WASM directly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;wasmtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;examples/async-generator.js | wasmtime target/wasm32-wasip1/debug/swc-wasi.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Continue learning about Rust and WebAssembly
&lt;/h2&gt;

&lt;p&gt;If you’d like to continue learning about Rust and WebAssembly, I highly recommend reading through the &lt;a href="https://rustwasm.github.io/docs/book/" rel="noopener noreferrer"&gt;Rust WebAssembly Book&lt;/a&gt; and then follow up with the &lt;a href="https://rustwasm.github.io/wasm-bindgen/introduction.html" rel="noopener noreferrer"&gt;The &lt;code&gt;wasm-bindgen&lt;/code&gt; Guide&lt;/a&gt; for the practical training material.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>rust</category>
      <category>tutorial</category>
      <category>wasi</category>
    </item>
  </channel>
</rss>
