Build a Wasm runtime

A browser converter consists of Wasm containing the Go format converters you select, plus a JavaScript runtime and Worker matching the compiler. Build from the root of this repository. You need Go 1.27 or later and Node.js; the TinyGo route also requires TinyGo. See Integrating into a frontend for connecting the runtime to the viewer.

TinyGo: select your formats

The npm @bdfkit/convert package uses a Worker containing the TinyGo runtime. The repository's build script writes a temporary Go entry point with blank imports for the chosen formats and compiles it to Wasm.

npm ci
node tools/build-npm-converters.mjs --formats pdf,epub,docx,csv --out ./public/my-converter.wasm

Pass comma-separated subdirectory names from converter/ to --formats. tsv is an alias for csv; one converter handles both CSV and TSV. For example, add PowerPoint and Parquet with --formats pdf,epub,docx,csv,pptx,parquet. When you include html or markdown, the script also applies the browser-specific goldmark adjustment in a temporary module.

To omit music conversion, leave out MML, MIDI, and MusicXML. To start from every format and exclude those three, use --formats all --without-music. The converter Wasm does not contain the renderer: add @bdfkit/viewer only when the app displays BDF. Piano roll and playback JS likewise require an additional @bdfkit/viewer/music import.

node tools/build-npm-converters.mjs --formats all --without-music --out ./public/my-converter.wasm

Measured on September 30, 2026 with TinyGo 0.42.0, the Wasm file alone was 20.09 MiB raw / 8.81 MiB gzip for All, versus 19.51 MiB raw / 8.58 MiB gzip without music. This saves 0.58 MiB raw / 0.23 MiB gzip. Run npm run size:npm -- --custom-wasm ./public/my-converter.wasm to measure a custom module with the same gzip level 9 method.

Put the resulting Wasm in your web app's public directory and load it through @bdfkit/convert. Its packaged Worker must also be served. To try the workspace version in this repository, first run npm run build && npm run build:wasm to generate the Worker (build:wasm builds the three standard presets too).

import { createConverter } from "@bdfkit/convert";

const converter = createConverter(new URL("/my-converter.wasm", location.href));
console.log((await converter.ready).map((format) => format.name));
// viewer.openFile(converter, await file.arrayBuffer(), { name: file.name, fonts: "/fonts/" });
// Call converter.terminate() when finished.

Build all standard presets with npm run build:wasm, or one with node tools/build-npm-converters.mjs --preset pdf-epub (office and all are also available). npm packages lists measured download sizes for the presets plus viewer.

Go: use the standard Go compiler

Go output needs Go's own wasm_exec.js. The @bdfkit/convert Worker embeds TinyGo's runtime, so use the repository's examples/common/convert-worker.ts arrangement for Go output. This example selects PDF, EPUB, Word, and CSV/TSV. Change the blank imports in formats.go to choose another combination.

Add converter/mml, converter/midi, and converter/musicxml only when music conversion is required. The rendering Worker is a separate asset from this converter Wasm.

mkdir -p cmd/my-bdfwasm public/lib
cp cmd/bdfwasm/main.go cmd/bdfwasm/fontfs.go cmd/my-bdfwasm/
cat > cmd/my-bdfwasm/formats.go <<'EOF'
//go:build js && wasm

package main

import (
    _ "github.com/shibukawa/bdf/converter/pdf"
    _ "github.com/shibukawa/bdf/converter/epub"
    _ "github.com/shibukawa/bdf/converter/docx"
    _ "github.com/shibukawa/bdf/converter/csv"
)
EOF
GOOS=js GOARCH=wasm go build -tags bdf_noconv -trimpath -ldflags='-s -w' \
  -o public/lib/converter.wasm ./cmd/my-bdfwasm
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" public/lib/wasm_exec.js
node --input-type=module -e 'import { bundle } from "./examples/common/build.mjs"; await bundle("./public/lib", [["examples/common/convert-worker.ts", "convert-worker"]], { format: "iife" })'

Serve public/lib/converter.wasm, wasm_exec.js, and convert-worker.js. Get the Go compiler and wasm_exec.js from the same Go installation. The Worker calls importScripts("./wasm_exec.js"), so serve the two JavaScript files from the same directory. Use ConverterClient from examples/common/convert.ts with that Worker. Its convert("/lib/converter.wasm", data, { name: file.name }) returns BDF bytes for Viewer.open({ kind: "buffer", buffer: ... }). See the working browser conversion sample.

When including HTML or Markdown in a Go build, rebuild the Go module above with the temporary go.mod generated by patchGoldmark(). This avoids a deep regular expression exhausting Safari's Worker stack at startup; the site build uses the same adjustment.

node --input-type=module -e 'import { patchGoldmark } from "./examples/common/build.mjs"; await patchGoldmark("cmd/my-bdfmod")'
GOOS=js GOARCH=wasm go build -tags bdf_noconv -trimpath -ldflags='-s -w' \
  -modfile=cmd/my-bdfmod/go.mod -o public/lib/converter.wasm ./cmd/my-bdfwasm

Fonts and delivery

Formats that lay out text, such as Office documents and EPUB, may need a font directory URL in the fonts conversion option. Serve fonts and an index.json there; copyFonts() in examples/common/build.mjs generates the index. Serve Wasm over HTTP with the application/wasm MIME type for the Go Worker, and allow Workers and WebAssembly in your content security policy. Inspect included formats with converter.ready for TinyGo, or bdfConverter.formats inside the Go Worker.