Close Menu
    Latest Post

    Build Resilient Generative AI Agents

    January 8, 2026

    Accelerating Stable Diffusion XL Inference with JAX on Cloud TPU v5e

    January 8, 2026

    Older Tech In The Browser Stack

    January 8, 2026
    Facebook X (Twitter) Instagram
    Trending
    • Build Resilient Generative AI Agents
    • Accelerating Stable Diffusion XL Inference with JAX on Cloud TPU v5e
    • Older Tech In The Browser Stack
    • If you hate Windows Search, try Raycast for these 3 reasons
    • The Rotel DX-5: A Compact Integrated Amplifier with Mighty Performance
    • Drones to Diplomas: How Russia’s Largest Private University is Linked to a $25M Essay Mill
    • Amazon’s 55-inch 4-Series Fire TV Sees First-Ever $100 Discount
    • Managing Cloudflare at Enterprise Scale with Infrastructure as Code and Shift-Left Principles
    Facebook X (Twitter) Instagram Pinterest Vimeo
    NodeTodayNodeToday
    • Home
    • AI
    • Dev
    • Guides
    • Products
    • Security
    • Startups
    • Tech
    • Tools
    NodeTodayNodeToday
    Home»Dev»WebAssembly with Go: Elevating Web Applications
    Dev

    WebAssembly with Go: Elevating Web Applications

    Samuel AlejandroBy Samuel AlejandroDecember 27, 2025No Comments14 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    src 1oh3wrt featured
    Share
    Facebook Twitter LinkedIn Pinterest Email

    WebAssembly (WASM) is generating significant discussion within the development community due to its extensive potential. This technology has proven valuable for enhancing open-source projects.

    Permify is an open-source infrastructure designed to assist developers in creating and managing granular permissions across their applications.

    This article explores the reasons and methods behind integrating WebAssembly (WASM) into the Permify Playground, highlighting the advantages gained from its collaboration with Golang.

    The Permify Playground functions as an interactive module for creating and testing authorization models.

    This post covers:

    • A brief explanation of WASM and the benefits of using it with Go.
    • A peek into what spurred the choice to integrate WASM in Permify.
    • WASM Implementation, including
    • Quick Warm Up: WASM Implementation with Go
    • Deeper Dive: Permify’s WASM Code Breakdown
    • Frontend: Steps to Embed Go WASM in a React Application

    By the end of this article, there should be a clearer understanding of how WASM’s capabilities were utilized for the project.

    Understanding WebAssembly

    WebAssembly (Wasm) is a key technology that allows fast and efficient code execution in web browsers, bridging the gap between web applications and the high performance usually found in native applications.

    1. Unveiling WebAssembly:

    Wasm operates as a low-level virtual machine, running compact binary code compiled from high-level programming languages.

    Primary Advantages:

    • Universal Browser Support: Thanks to its support from all major browsers, Wasm delivers consistent performance across diverse platforms.
    • Near-Native Performance: Intentionally designed to execute binary code at a speed akin to native applications, Wasm enhances the responsiveness of web applications considerably.

    Go (Golang) was strategically integrated into the core of the open-source Permify project, chosen for its static typing, concurrency handling, and performance optimization. During the development of the Permify Playground, WebAssembly emerged as a critical component.

    2. Blending Go & WebAssembly:

    • Characteristics of Go: Celebrated for its optimal performance and concurrency handling capabilities, Go has carved a sturdy standing within the developer community.
    • Synergy with WebAssembly: The translation of Go code into WebAssembly enables developers to effectively utilize Go’s robust performance and concurrency management directly within the browser, propelling the creation of powerful, efficient, and scalable web applications.

    The integration of Go and WebAssembly is further explored by examining why Wasm was chosen for the Permify Playground and the significant benefits it provided.

    Why WebAssembly?

    Developing the Permify Playground raised a crucial question: how to demonstrate its features without the complexities and maintenance issues of traditional server architectures? WebAssembly provided an effective solution. By adopting this binary instruction format, the project could:

    • Execute In-Browser: Permify’s playground could operate straight within the browser, sidestepping server maintenance overheads and repetitive API calls, and notably, making ongoing maintenance a breeze in comparison to older server-based approaches.
    • Achieve Peak Performance: Employing WebAssembly ensures that the Go application operates with a level of performance that competes with native applications, enhancing user interactions and bolstering response times.

    Harvesting Technical Benefits and Gathering User Feedback

    Using WebAssembly in the Permify Playground resulted in clear technical advantages and positive community reception:

    • Swift Execution: By side-stepping server interactions and deploying WebAssembly in-browser, ultra-fast response times have been delivered.
    • Uncomplicated User Interface: Centralizing the playground in the browser has dispelled complexities associated with multi-tool workflows, delivering a clean and straightforward user experience.
    • Community Validation: The affirming feedback and positive reception from the developer community stand as validation of the technological choices and implementations.

    The following sections delve deeper into the technical aspects, feedback, and lessons learned from this WebAssembly endeavor.

    WASM Implementation with Go

    Before examining Permify’s use of WebAssembly (WASM) and Go, understanding their combination in a sample application is beneficial. A step-by-step guide to integrating them is provided, preparing for a deeper look into Permify’s implementation.

    1. Transforming Go into WebAssembly:

    • Steps:
    1. To begin, set the WebAssembly build target in Go:
    GOOS=js GOARCH=wasm go build -o main.wasm main.go
    1. Next, apply optimizations to reduce file size and improve performance:
    wasm-opt main.wasm --enable-bulk-memory -Oz -o play.wasm
    • Handling Events:

    If a Go function needs to respond to a button click from a web page:

    package main
    
    import "syscall/js"
    
    func registerCallbacks() {
        js.Global().Set("handleClick", js.FuncOf(handleClick))
    }
    
    func handleClick(this js.Value, inputs []js.Value) interface{} {
        println("Button clicked!")
        return nil
    }

    In the HTML, after loading the WebAssembly module:

    <button onclick="window.handleClick()">Click me</button>

    2. Integrating with Web Pages:

    • Initializing Wasm:

    Ensure the wasm_exec.js script is linked, then instantiate the Wasm module:

    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("play.wasm"), go.importObject).then((result) => {
            go.run(result.instance);
        });
    </script>
    • Interacting with the DOM:

    Accessing and modifying web elements is fundamental. For example, changing a paragraph element’s content from Go would appear as follows:

    func updateDOMContent() {
        document := js.Global().Get("document")
        element := document.Call("getElementById", "myParagraph")
        element.Set("innerText", "Updated content from Go!")
    }

    3. The Gains: Efficiency & Speed:

    • Go’s Goroutines in the Browser:

    Consider multiple data fetch operations running simultaneously without blocking the main thread:

    func fetchData(url string, ch chan string) {
        // Simulate data fetch.
        ch <- "Data from " + url
    }
    
    func main() {
        ch := make(chan string)
        go fetchData("<https://api.example1.com>", ch)
        go fetchData("<https://api.example2.com>", ch)
    
        data1 := <-ch
        data2 := <-ch
        println(data1, data2)
    }

    Exploring Go and WebAssembly (WASM) reveals a powerful combination, merging Go’s concurrent processing with WASM’s fast client-side execution. The sample application demonstrates how these technological strengths can be applied to a scalable, real-world authorization system like Permify.

    Deeper Dive: Permify’s WASM Code Breakdown

    A deeper look into the core of the WebAssembly integration involves exploring key segments of the Go-based WASM code.

    1. Setting up the Go-to-WASM Environment

    involves preparing and specifying Go code for compilation to a WebAssembly runtime.

    // go:build wasm
    // +build wasm

    These lines act as directives for the Go compiler, indicating that the subsequent code is intended for a WebAssembly runtime environment. Specifically:

    • //go:build wasm: A build constraint ensuring the code is compiled only for WASM targets, adhering to modern syntax.
    • // +build wasm: An analogous constraint, utilizing older syntax for compatibility with prior Go versions.

    Essentially, these directives instruct the compiler to include this code segment only when compiling for a WebAssembly architecture, ensuring proper setup and function within that specific runtime.

    2. Bridging JavaScript and Go with the run Function

    package main
    
    import (
     "context"
     "encoding/json"
     "syscall/js"
    
     "google.golang.org/protobuf/encoding/protojson"
    
     "github.com/Permify/permify/pkg/development"
    )
    
    var dev *development.Development
    
    func run() js.Func {
      // The `run` function returns a new JavaScript function
      // that wraps the Go function.
     return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    
      // t will be used to store the unmarshaled JSON data.
      // The use of an empty interface{} type means it can hold any type of value.
      var t interface{}
    
      // Unmarshal JSON from JavaScript function argument (args[0]) to Go's data structure (map).
      // args[0].String() gets the JSON string from the JavaScript argument,
      // which is then converted to bytes and unmarshaled (parsed) into the map `t`.
      err := json.Unmarshal([]byte(args[0].String()), &t)
    
      // If an error occurs during unmarshaling (parsing) the JSON,
      // it returns an array with the error message "invalid JSON" to JavaScript.
      if err != nil {
       return js.ValueOf([]interface{}{"invalid JSON"})
      }
    
      // Attempt to assert that the parsed JSON (`t`) is a map with string keys.
      // This step ensures that the unmarshaled JSON is of the expected type (map).
      input, ok := t.(map[string]interface{})
    
      // If the assertion is false (`ok` is false),
      // it returns an array with the error message "invalid JSON" to JavaScript.
      if !ok {
       return js.ValueOf([]interface{}{"invalid JSON"})
      }
    
      // Run the main logic of the application with the parsed input.
      // It’s assumed that `dev.Run` processes `input` in some way and returns any errors encountered during that process.
      errors := dev.Run(context.Background(), input)
    
      // If no errors are present (the length of the `errors` slice is 0),
      // return an empty array to JavaScript to indicate success with no errors.
      if len(errors) == 0 {
       return js.ValueOf([]interface{}{})
      }
    
      // If there are errors, each error in the `errors` slice is marshaled (converted) to a JSON string.
      // `vs` is a slice that will store each of these JSON error strings.
      vs := make([]interface{}, 0, len(errors))
    
      // Iterate through each error in the `errors` slice.
      for _, r := range errors {
       // Convert the error `r` to a JSON string and store it in `result`.
       // If an error occurs during this marshaling, it returns an array with that error message to JavaScript.
       result, err := json.Marshal(r)
       if err != nil {
        return js.ValueOf([]interface{}{err.Error()})
       }
       // Add the JSON error string to the `vs` slice.
       vs = append(vs, string(result))
      }
    
      // Return the `vs` slice (containing all JSON error strings) to JavaScript.
      return js.ValueOf(vs)
     })
    }

    In Permify, the run function is crucial for bridging JavaScript inputs and Go’s processing capabilities. It manages real-time JSON data interchange, ensuring Permify’s core functionalities are smoothly and instantly accessible through a browser interface.

    Digging into run:

    • JSON Data Interchange: Translating JavaScript inputs into a format utilizable by Go, the function unmarshals JSON, transferring data between JS and Go, assuring that the robust processing capabilities of Go can seamlessly manipulate browser-sourced inputs.
    • Error Handling: Ensuring clarity and user-awareness, it conducts meticulous error-checking during data parsing and processing, returning relevant error messages back to the JavaScript environment to ensure user-friendly interactions.
    • Contextual Processing: By employing dev.Run, it processes the parsed input within a certain context, managing application logic while handling potential errors to assure steady data management and user feedback.
    • Bidirectional Communication: As errors are marshaled back into JSON format and returned to JavaScript, the function ensures a two-way data flow, keeping both environments in synchronized harmony.

    Therefore, by skillfully managing data, handling errors, and maintaining a fluid two-way communication channel, the run function acts as an essential bridge, connecting JavaScript and Go to ensure Permify’s smooth, real-time operation within a browser interface. This interaction enhances user experience and utilizes the strengths of both JavaScript and Go within the Permify environment.

    3. Main Execution and Initialization

    // Continuing from the previously discussed code...
    
    func main() {
      // Instantiate a channel, 'ch', with no buffer, acting as a synchronization point for the goroutine.
      ch := make(chan struct{}, 0)
    
      // Create a new instance of 'Container' from the 'development' package and assign it to the global variable 'dev'.
      dev = development.NewContainer()
    
      // Attach the previously defined 'run' function to the global JavaScript object,
      // making it callable from the JavaScript environment.
      js.Global().Set("run", run())
    
      // Utilize a channel receive expression to halt the 'main' goroutine, preventing the program from terminating.
      <-ch
    }
    1. ch := make(chan struct{}, 0): A synchronization channel is created to coordinate the activity of goroutines (concurrent threads in Go).
    2. dev = development.NewContainer(): Initializes a new container instance from the development package and assigns it to dev.
    3. js.Global().Set("run", run()): Exposes the Go run function to the global JavaScript context, enabling JavaScript to call Go functions.
    4. <-ch: Halts the main goroutine indefinitely, ensuring that the Go WebAssembly module remains active in the JavaScript environment.

    In summary, the code sets up a Go environment within WebAssembly that exposes specific functionality (the run function) to JavaScript, remaining active and available for calls from the JavaScript environment.

    Building the Go Code into a WASM Module

    Before exploring Permify’s functionalities, it is important to outline the steps for converting Go code into a WASM module, preparing it for browser execution.

    For those interested in the complete Go codebase, the GitHub repository is available: Permify Wasm Code.

    1. Compiling to WASM

    Begin the transformation of a Go application into a WASM binary with this command:

    GOOS=js GOARCH=wasm go build -o permify.wasm main.go

    This directive instructs the Go compiler to produce a .wasm binary suitable for JavaScript environments, using main.go as the source. The resulting permify.wasm is a compact representation of Go’s capabilities, ready for web deployment.

    2. WASM Exec JS

    Alongside the WASM binary, the Go ecosystem provides the essential JavaScript file named wasm_exec.js. This file is crucial for initializing and enabling the WASM module within a browser. It is typically found within the Go installation, under misc/wasm.

    For convenience, wasm_exec.js is hosted for direct access: wasm_exec.

    cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

    With these key assets—the WASM binary and its accompanying JavaScript—the integration into the frontend is prepared.

    Steps to Embed Go WASM in a React Application

    1. Setting Up the React Application Structure

    To begin, ensure a directory structure that clearly separates WebAssembly-related code from the rest of the application. Based on the provided structure, the loadWasm folder appears to be central to this functionality:

    loadWasm/
    │
    ├── index.tsx            // Your main React component that integrates WASM.
    ├── wasm_exec.js         // Provided by Go, bridges the gap between Go's WASM and JS.
    └── wasmTypes.d.ts       // TypeScript type declarations for WebAssembly.

    For the complete structure and file specifics, refer to the Permify Playground on GitHub.

    2. Establishing Type Declarations

    Within wasmTypes.d.ts, global type declarations extend the Window interface to recognize new methods introduced by Go’s WebAssembly:

    declare global {
      export interface Window {
        Go: any;
        run: (shape: string) => any[];
      }
    }
    export {};

    This ensures TypeScript recognizes the Go constructor and the run method when invoked on the global window object.

    3. Preparing the WebAssembly Loader

    In index.tsx, several critical tasks are performed:

    • Import Dependencies: First, the required JS and TypeScript declarations are imported:
    import "./wasm_exec.js";
    import "./wasmTypes.d.ts";
    • WebAssembly Initialization: The asynchronous function loadWasm manages the entire process:
    async function loadWasm(): Promise<void> {
      const goWasm = new window.Go();
      const result = await WebAssembly.instantiateStreaming(
        fetch("play.wasm"),
        goWasm.importObject
      );
      goWasm.run(result.instance);
    }

    Here, new window.Go() initializes the Go WASM environment. WebAssembly.instantiateStreaming fetches, compiles, and creates an instance of the WASM module. Finally, goWasm.run activates the WASM module.

    • React Component with Loader UI: The LoadWasm component utilizes the useEffect hook to asynchronously load WebAssembly when the component mounts:
    export const LoadWasm: React.FC<React.PropsWithChildren<{}>> = (props) => {
      const [isLoading, setIsLoading] = React.useState(true);
    
      useEffect(() => {
        loadWasm().then(() => {
          setIsLoading(false);
        });
      }, []);
    
      if (isLoading) {
        return (
          <div className="wasm-loader-background h-screen">
            <div className="center-of-screen">
              <SVG src={toAbsoluteUrl("/media/svg/rocket.svg")} />
            </div>
          </div>
        );
      } else {
        return <React.Fragment>{props.children}</React.Fragment>;
      }
    };

    During loading, an SVG rocket is displayed to indicate ongoing initialization. This feedback is important, as users might otherwise be unsure about the background processes. Once loading is complete, child components or content will render.

    4. Calling WebAssembly Functions

    If the Go WASM exposes a method named run, it can be invoked as follows:

    function Run(shape) {
      return new Promise((resolve) => {
        let res = window.run(shape);
        resolve(res);
      });
    }

    This function serves as a bridge, enabling the React frontend to communicate with the Go backend logic encapsulated within the WASM.

    5. Implementing the Run Button in React

    To integrate a button that triggers the WebAssembly function upon clicking, follow these steps:

    1. Creating the Button Component

    First, a simple React component with a button is created:

    import React from "react";
    
    type RunButtonProps = {
      shape: string;
      onResult: (result: any[]) => void;
    };
    
    function RunButton({ shape, onResult }: RunButtonProps) {
      const handleClick = async () => {
        let result = await Run(shape);
        onResult(result);
      };
    
      return <button onClick={handleClick}>Run WebAssembly</button>;
    }

    In the code above, the RunButton component accepts two properties:

    • shape: The shape argument to pass to the WebAssembly run function.
    • onResult: A callback function that receives the result of the WebAssembly function and can be used to update the state or display the result in the UI.
    1. Integrating the Button in the Main Component

    Now, integrate the RunButton into the main component (or desired location):

    import React, { useState } from "react";
    import RunButton from "./path_to_RunButton_component"; // Replace with the actual path
    
    function App() {
      const [result, setResult] = useState<any[]>([]);
    
      // Define the shape content
      const shapeContent = {
        schema: `|-
        entity user {}
    
        entity account {
            relation owner @user
            relation following @user
            relation follower @user
    
            attribute public boolean
            action view = (owner or follower) or public  
        }
    
        entity post {
            relation account @account
    
            attribute restricted boolean
    
            action view = account.view
    
            action comment = account.following not restricted
            action like = account.following not restricted
        }`,
        relationships: [
          "account:1#owner@user:kevin",
          "account:2#owner@user:george",
          "account:1#following@user:george",
          "account:2#follower@user:kevin",
          "post:1#account@account:1",
          "post:2#account@account:2",
        ],
        attributes: [
          "account:1$public|boolean:true",
          "account:2$public|boolean:false",
          "post:1$restricted|boolean:false",
          "post:2$restricted|boolean:true",
        ],
        scenarios: [
          {
            name: "Account Viewing Permissions",
            description:
              "Evaluate account viewing permissions for 'kevin' and 'george'.",
            checks: [
              {
                entity: "account:1",
                subject: "user:kevin",
                assertions: {
                  view: true,
                },
              },
            ],
          },
        ],
      };
    
      return (
        <div>
          <RunButton shape={JSON.stringify(shapeContent)} onResult={setResult} />
          <div>
            Results:
            <ul>
              {result.map((item, index) => (
                <li key={index}>{item}</li>
              ))}
            </ul>
          </div>
        </div>
      );
    }

    In this example, App is a component containing the RunButton. When the button is clicked, the result from the WebAssembly function appears in a list below it.

    Conclusion

    This exploration has detailed the integration of WebAssembly with Go, highlighting its potential for enhanced web development and optimized browser-based user interactions.

    The process involved setting up the Go environment, converting Go code to WebAssembly, and executing it within a web context, which led to the interactive platform available at play.permify.co.

    This platform serves as both an example and a demonstration of the tangible and powerful capabilities achieved by combining these technological domains.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleUnderstanding iPad Battery Health and When to Consider a Replacement
    Next Article The fast and the future-focused are revolutionizing motorsport
    Samuel Alejandro

    Related Posts

    Dev

    Older Tech In The Browser Stack

    January 8, 2026
    Dev

    CSS Wrapped 2025

    January 8, 2026
    Tools

    Design System Annotations: Why Accessibility is Often Overlooked in Component Design (Part 1)

    January 7, 2026
    Add A Comment
    Leave A Reply Cancel Reply

    Latest Post

    ChatGPT Mobile App Surpasses $3 Billion in Consumer Spending

    December 21, 202512 Views

    Automate Your iPhone’s Always-On Display for Better Battery Life and Privacy

    December 21, 202510 Views

    Creator Tayla Cannon Lands $1.1M Investment for Rebuildr PT Software

    December 21, 20259 Views
    Stay In Touch
    • Facebook
    • YouTube
    • TikTok
    • WhatsApp
    • Twitter
    • Instagram
    About

    Welcome to NodeToday, your trusted source for the latest updates in Technology, Artificial Intelligence, and Innovation. We are dedicated to delivering accurate, timely, and insightful content that helps readers stay ahead in a fast-evolving digital world.

    At NodeToday, we cover everything from AI breakthroughs and emerging technologies to product launches, software tools, developer news, and practical guides. Our goal is to simplify complex topics and present them in a clear, engaging, and easy-to-understand way for tech enthusiasts, professionals, and beginners alike.

    Latest Post

    Build Resilient Generative AI Agents

    January 8, 20260 Views

    Accelerating Stable Diffusion XL Inference with JAX on Cloud TPU v5e

    January 8, 20260 Views

    Older Tech In The Browser Stack

    January 8, 20260 Views
    Recent Posts
    • Build Resilient Generative AI Agents
    • Accelerating Stable Diffusion XL Inference with JAX on Cloud TPU v5e
    • Older Tech In The Browser Stack
    • If you hate Windows Search, try Raycast for these 3 reasons
    • The Rotel DX-5: A Compact Integrated Amplifier with Mighty Performance
    Facebook X (Twitter) Instagram Pinterest
    • About Us
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    • Disclaimer
    • Cookie Policy
    © 2026 NodeToday.

    Type above and press Enter to search. Press Esc to cancel.