Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

morse-wasm-encoder-decoder

International Morse code encoder/decoder, written in Rust and compiled to WebAssembly. Built as a learning vehicle for the Rust → wasm pipeline; intended to be reusable from any web app that needs Morse encoding/decoding without a JS implementation.

morse-wasm-encoder-decoder.demo.mov

Status

  • 7 unit tests passing on the native target.
  • Builds cleanly to wasm32-unknown-unknown via wasm-pack.
  • Output bundle: ~23 KB .wasm + ~7 KB JS glue.

Prerequisites

Rust toolchain and wasm-pack. See INSTALL.md for the exact setup. Quick sanity check:

rustc --version
cargo --version
wasm-pack --version

Project layout

morse-wasm/
├── Cargo.toml           # crate manifest, declares cdylib for wasm
├── INSTALL.md           # toolchain setup notes
├── README.md            # this file
├── src/
│   └── lib.rs           # encode/decode + tests + alphabet table
├── demo/
│   └── index.html       # self-contained demo page
└── pkg/                 # generated by wasm-pack, committed for direct consumption
    ├── morse_wasm.js
    ├── morse_wasm_bg.wasm
    ├── morse_wasm.d.ts
    └── package.json

Build

From the repo root:

wasm-pack build --target web --release

This emits the pkg/ directory. Default --out-dir is pkg, so no flag needed.

What it does, end to end:

  1. Compiles the crate for wasm32-unknown-unknown (auto-installs the target if missing).
  2. Runs wasm-bindgen to generate the JS glue that marshals strings across the JS↔Wasm boundary.
  3. Optimizes the .wasm with wasm-opt.
  4. Drops everything in pkg/.

--target web emits an ES module loadable by <script type="module"> directly, no bundler required. Other options if needed: bundler (webpack/rollup), nodejs, no-modules. See wasm-pack build --help.

After build, pkg/ contains:

File Purpose
morse_wasm.js ES module with init() + encode() + decode() exports
morse_wasm_bg.wasm the compiled WebAssembly binary
morse_wasm.d.ts TypeScript declarations for both files
morse_wasm_bg.wasm.d.ts type info for the raw wasm exports
package.json npm manifest, ready to publish

Tests

cargo test

Tests run on the native target (no wasm involved), validating the encode/decode logic. Browser-targeted tests would use wasm-bindgen-test and wasm-pack test --headless --chrome; not wired up yet.

Running the demo

The demo at demo/index.html is self-contained — no Express, no bundler. But because it loads a .wasm file, browsers refuse to load it from a file:// URL. Serve it over HTTP:

wasm-pack build --target web --release   # only needed once, or after src/ changes
python3 -m http.server -d . 8000          # any static server works

Then open http://localhost:8000/demo/.

The demo imports ../pkg/morse_wasm.js, so pkg/ must exist alongside demo/.

API

Both functions are #[wasm_bindgen]-exported and callable from JS as plain functions taking and returning strings.

encode(text: string) → string

  • Maps each character to its Morse representation.
  • Letters within a word are separated by a single space.
  • Words are separated by / (slash with surrounding spaces).
  • Input is uppercased before lookup, so case is ignored.
  • Whitespace runs collapse to a single word boundary.
  • Characters not in the alphabet are silently skipped.
encode("Hello, world!");
// → ".... . .-.. .-.. --- --..-- / .-- --- .-. .-.. -.. -.-.--"

decode(morse: string) → string

  • Splits on / to find word boundaries, then on whitespace within each word for letter boundaries.
  • Each token is looked up in the reverse alphabet.
  • Unknown tokens are silently skipped.
  • Output is uppercase ASCII; words are joined with single spaces.
decode(".... . .-.. .-.. --- / .-- --- .-. .-.. -..");
// → "HELLO WORLD"

Round-trip

decode(encode(s)) equals s.toUpperCase() whenever every character of s is in the alphabet (after uppercasing). Whitespace runs are collapsed.

encode(decode(m)) equals the canonical form of m (single spaces, / between words) when every token of m is a valid Morse code in the alphabet.

Alphabet

53 entries: A–Z, 0–9, and 17 punctuation marks (. , ? ' ! / ( ) & : ; = + - _ " @). The full table lives in src/lib.rs.

Using morse-wasm from another app

The built artifacts in pkg/ are committed, so consumers can use them without a Rust toolchain installed. Pick one of these:

A. Clone next to the consumer app. Clone this repo as a sibling of your app, then reference pkg/morse_wasm.js directly. Either copy pkg/ into your app's static dir, or set up your build to symlink/serve it.

B. Git submodule. Add this repo as a submodule of the consumer repo. The consumer references pkg/morse_wasm.js from there.

C. Rebuild into the consumer. If you have Rust and wasm-pack available, you can rebuild on demand:

wasm-pack build --target web --release --out-dir ../my-app/static/morse-wasm

That bypasses the committed pkg/ and emits a fresh build into the consumer's static dir.

D. Future: npm publish. cd pkg && npm publish would put it on npm under the name in pkg/package.json. Consumers then npm install morse-wasm and import { encode, decode } from 'morse-wasm'. Requires an npm account.

How the wasm pipeline works (mental model)

  1. The crate sets crate-type = ["cdylib", "rlib"] in Cargo.toml. cdylib is what rustc needs to emit a standalone wasm module on the wasm32-unknown-unknown target; rlib lets cargo test link it normally on the host.
  2. #[wasm_bindgen] macros on encode and decode cause the macro expansion to emit metadata describing the JS-facing signatures (string in, string out).
  3. After cargo build, wasm-pack invokes the wasm-bindgen CLI, which reads that metadata and generates morse_wasm.js — a thin JS module that knows how to copy strings into the wasm linear memory, call the underlying numeric exports, and read result strings back out.
  4. wasm-opt shrinks the binary.
  5. In the browser, import init, { encode, decode } from './morse_wasm.js' loads the JS glue. await init() fetches and instantiates the .wasm and wires up the exports. After that, encode(s) looks like a normal JS function call.

The Rust code has no idea it is talking to JS — it just takes a &str and returns a String. All the marshalling lives in the generated glue.

Reproducing from scratch

git clone <this repo>
cd morse-wasm
# Set up Rust + wasm-pack per INSTALL.md
cargo test
wasm-pack build --target web --release
python3 -m http.server -d . 8000
# open http://localhost:8000/demo/

About

International Morse code encoder/decoder, written in Rust and compiled to WebAssembly.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages