Skip to content

MAC Address Generator — JavaScript source

Generate random EUI-48 MAC addresses with a chosen separator, optional OUI prefix, uppercase formatting, and a locally-administered flag. Runs entirely in your browser with crypto-grade randomness.

This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

/**
 * mac-address-generator - random EUI-48 MAC address generator.
 *
 * Language:   JavaScript (ES2020+, runs unmodified in Node 15+ and modern browsers)
 * Source:     CosmoDev polyglot showcase port of the MAC Address Generator tool,
 *             ported from src/lib/mac-generator.ts (the canonical TypeScript
 *             implementation), kept in lock-step with the Go twin at
 *             cli/mac-address-generator/mac-address-generator.go.
 * License:    display source - part of CosmoDev's polyglot tool pages.
 *
 * Design goals:
 *   - Pure + deterministic; never throws on randomness, only on a malformed OUI.
 *   - Functionally equivalent to the TS reference: same inputs -> same outputs.
 *   - Self-contained: stdlib only (no npm dependencies).
 *
 * API note: this port mirrors the TS canonical lib's public API, the
 * deterministic, RNG-injectable superset of the Go twin's. The Go twin draws
 * from crypto/rand directly (non-injectable), so its own suite asserts only
 * structural properties (regex shape, OUI prefix, U/L bit). Like the TS lib,
 * this port accepts an injected rng returning a number in [0, 1), letting the
 * showcase tests below assert exact MAC strings.
 *
 * Pipeline: (optional OUI -> 3 octets) -> 3 random octets -> (force U/L bit) ->
 * join 6 octets as 2-digit lowercase hex with the separator -> (uppercase). A
 * malformed OUI (not 6 hex digits after stripping non-hex chars) throws.
 */

'use strict';

/**
 * Default RNG: a 32-bit draw from the Web Crypto API mapped to [0, 1) - mirrors
 * the TS default `crypto.getRandomValues(new Uint32Array(1))[0] / 2**32`. Works
 * in Node 15+ (global crypto) and all modern browsers.
 *
 * @returns {number}
 */
function defaultRng() {
  // crypto is global on Node 15+ and in browsers; guard for completeness.
  const c = (typeof globalThis !== 'undefined' && globalThis.crypto) || crypto;
  return c.getRandomValues(new Uint32Array(1))[0] / 2 ** 32;
}

/**
 * Two-digit lowercase hex, mirroring TS `n.toString(16).padStart(2, '0')`.
 *
 * @param {number} n
 * @returns {string}
 */
function hex(n) {
  return n.toString(16).padStart(2, '0');
}

/**
 * Options shape. Keys are all optional.
 * @typedef {Object} MacOptions
 * @property {(':' | '-' | '.' | '')} [separator] Joining chars. Defaults to ':'. Empty string concatenates.
 * @property {boolean} [uppercase] Uppercase the hex letters in the result.
 * @property {string} [oui] Optional 6-hex-digit OUI prefix (separators tolerated, stripped first).
 * @property {boolean} [locallyAdministered] Force the U/L bit (0x02) of the first octet.
 * @property {() => number} [rng] Random source returning a number in [0, 1).
 */

/**
 * Build a random EUI-48 MAC address. The JS twin of `generateMac()` in
 * src/lib/mac-generator.ts.
 *
 * @param {MacOptions} [options={}]
 * @returns {string}
 */
function generateMac(options = {}) {
  const rng = options.rng ?? defaultRng;
  const sep = options.separator ?? ':';
  const octets = [];

  if (options.oui) {
    const clean = options.oui.replace(/[^0-9a-fA-F]/g, '');
    if (clean.length !== 6) throw new Error(`OUI must be 6 hex digits, got "${options.oui}"`);
    octets.push(
      parseInt(clean.slice(0, 2), 16),
      parseInt(clean.slice(2, 4), 16),
      parseInt(clean.slice(4, 6), 16),
    );
  } else {
    octets.push(Math.floor(rng() * 256), Math.floor(rng() * 256), Math.floor(rng() * 256));
  }
  octets.push(Math.floor(rng() * 256), Math.floor(rng() * 256), Math.floor(rng() * 256));

  if (options.locallyAdministered) {
    octets[0] = (octets[0] & 0b11111101) | 0b00000010; // set U/L bit, clear multicast
  }

  let mac = octets.map(hex).join(sep);
  if (options.uppercase) mac = mac.toUpperCase();
  return mac;
}

// CommonJS export so the file is consumable from Node without a build step,
// while staying dependency-free and framework-agnostic.
module.exports = { generateMac, hex, defaultRng };

// ---------- showcase tests (mirror src/lib/mac-generator.test.ts) ----------
// Run: node javascript.js
if (require.main === module) {
  const assert = require('assert');
  assert.strictEqual(generateMac({ rng: () => 0 }), '00:00:00:00:00:00');
  // separators: dash and concatenate
  assert.strictEqual(generateMac({ rng: () => 0, separator: '-' }), '00-00-00-00-00-00');
  assert.strictEqual(generateMac({ rng: () => 0, separator: '' }), '000000000000');
  // floor(0.7 * 256) === 179 === 0xb3
  assert.strictEqual(generateMac({ rng: () => 0.7, uppercase: true }), 'B3:B3:B3:B3:B3:B3');
  // OUI prefix (embedded separators stripped)
  assert.strictEqual(generateMac({ rng: () => 0, oui: 'aa-bb-cc' }), 'aa:bb:cc:00:00:00');
  // locally administered: floor(0.5 * 256) === 0x80 -> 0x82
  assert.strictEqual(generateMac({ rng: () => 0.5, locallyAdministered: true }), '82:80:80:80:80:80');
  // malformed OUI -> throws
  assert.throws(() => generateMac({ rng: () => 0, oui: 'abc' }));
  console.log('ok');
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →