Pi WebAssembly

The following is a Pi calculator delivered as a WebAssembly (WASM). Where our API based random Pi generator makes calls to a dedicated Pi calculation server, this WebAssembly approach embeds a Go based calculator that runs in a visitor’s browser.


The Go code for this web assembly is below:

Go
//go:build js && wasm

package main

import (
	"math/big"
	"syscall/js"
)

// CalculatePi returns Pi to `digits` decimal places using Gauss-Legendre algorithm.
func CalculatePi(this js.Value, args []js.Value) interface{} {
	digits := args[0].Int()
	precision := uint(digits * 4) // bits; not as fine as MPFR, but works for demo

	one := big.NewFloat(1).SetPrec(precision)
	two := big.NewFloat(2).SetPrec(precision)
	four := big.NewFloat(4).SetPrec(precision)

	// a = 1
	a := new(big.Float).SetPrec(precision).Set(one)
	// b = 1 / sqrt(2)
	b := new(big.Float).SetPrec(precision)
	b.Sqrt(two)
	b.Quo(one, b)
	// t = 1/4
	t := new(big.Float).SetPrec(precision).Set(one)
	t.Quo(t, four)
	// p = 1
	p := new(big.Float).SetPrec(precision).Set(one)

	// Iterative Process (fixed 8-10 iterations for convergence)
	var tmp, aNext, bNext, tNext, piApprox = new(big.Float), new(big.Float), new(big.Float), new(big.Float), new(big.Float)
	for i := 0; i < 8; i++ {
		// aNext = (a + b)/2
		aNext.Add(a, b)
		aNext.Quo(aNext, two)
		// bNext = sqrt(a*b)
		bNext.Mul(a, b)
		bNext.Sqrt(bNext)
		// tNext = t - p * (a - aNext)^2
		tmp.Sub(a, aNext)
		tmp.Mul(tmp, tmp)
		tmp.Mul(tmp, p)
		tNext.Sub(t, tmp)
		// p = 2*p
		p.Mul(p, two)
		// update
		a.Set(aNext)
		b.Set(bNext)
		t.Set(tNext)
	}

	// pi = (a + b)^2 / (4*t)
	piApprox.Add(a, b)
	piApprox.Mul(piApprox, piApprox)
	tmp.Mul(four, t)
	piApprox.Quo(piApprox, tmp)

	// Format result
	piStr := piApprox.Text('f', digits)
	return js.ValueOf(piStr)
}

func main() {
	js.Global().Set("calculatePiGo", js.FuncOf(CalculatePi))
	select {} // prevent exit
}

The following can be used in a WordPress Guttenberg HTML block:

HTML
<div>
  <label for="digits">Digits of π to calculate (max 500):</label>
  <input id="digits" type="number" value="10" min="1" max="500" />
  <button id="calc-btn">Calculate</button>
  <pre id="result"></pre>
</div>

<script src="https://picalculator.app/wp-content/uploads/wasm_exec.js"></script>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const go = new Go();
    WebAssembly.instantiateStreaming(
      fetch("https://picalculator.app/wp-content/uploads/pi.wasm"),
      go.importObject
    ).then((result) => {
      go.run(result.instance);

      document.getElementById('calc-btn').onclick = function () {
        let digits = parseInt(document.getElementById('digits').value, 10);

        // Enforce digit limits
        if (digits > 500) digits = 500;
        if (digits < 1 || isNaN(digits)) digits = 10;
        document.getElementById('digits').value = digits;

        const pi = window.calculatePiGo(digits);

        // Format Pi into 20-digit blocks, 2 blocks per line
        const raw = pi.toString();            // Full Pi string, e.g., "3.1415..."
        const head = raw.slice(0, 2);         // "3."
        const rest = raw.slice(2);            // Everything after "3."

        const blocks = rest.match(/.{1,20}/g) || [];  // Split into 20-digit blocks
        let formattedRows = [];

        for (let i = 0; i < blocks.length; i += 2) {
          const block1 = blocks[i];
          const block2 = blocks[i + 1] || '';
          const row = (i === 0 ? '' : '  ') + block1 + ' ' + block2;  // 2-space indent from 2nd line
          formattedRows.push(row);
        }

        const formatted = head + formattedRows.join('\n');  // Combine with newlines
        document.getElementById('result').textContent = formatted;
      };
    });
  });
</script>

Compiling a WASM in Go can be accomplished with this BASH command. Don't forget to copy the execution JavaScript file that Go provides!

Bash
GOOS=js GOARCH=wasm go build -o main.wasm
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" /path/to/wordpress/js/