Goodbye, .xyz... hello Twitter bots!

2021-11-29 - Reading time: 2 minutes

It's mostly done.

Almost all references to "" are replaced with "". Most things should forward appropriately, including email. I'll have the domain for years to come, so it'll never truly be gone...

As much as my beloved ".xyz" domain made me happy (I do love me some X's and Z's), it has an unfortunate stigma of being from the wrong side of the digital tracks. Prone to `*.xyz` being blacklisted, and so forth.

It was never a HUGE deal, but I might as well rip the bandage off sooner than later.

Meanwhile, I've also taken the time to make some revisions to site layout. Kind of surprised at how slow Bludit releases are, and the current 4.x that's just going into beta didn't seem to have features I'm interested in.

Maybe I'll just fork it for personal use? 🤔

Also meanwhile, over the Thanksgiving week, I launched my first two Twitter bots: @20MinutesBot and @TheShermometer. The former for the 1980s ABC TV series, Max Headroom, and the latter for The Critic. Both of which are instances of a Python script pumped out on Tuesday that uses the Twitter API to posts random images from a folder of images and metadata every 30 minutes.

It's simple, but boy is it fun. Like me!

Eeh, I'm not really that fun.

ToneDef 21

2021-06-06 - Reading time: ~1 minute

After a long hiatus, I finally have a good build workflow for Android again -- which means I can push out some updates to ToneDef!

Presenting build 21!

This was more of a "proof of concept" build with the beginning of some CI to automate checks, etc.  I've already started working on the audio "popping" bug, and looking into some other reported issues. Those are being saved for 22.

What's new:

  • Added French tones to the 'extras' section
  • Added link back to my blog (as seen below)
  • Minor layout updates, including removing action bar from most screens (will remove the rest later)
  • Code cleanup, refactoring, and other behind the scenes updates

X-Day Countdown

2021-04-04 - Reading time: ~1 minute

A couple years ago, I created an Android Wear (now "Wear OS") watchface (on Google Play) that threw the gaze of "Bob" upon you while you read the time. If you tapped the clock, it would also show you a countdown to that magic hour of 7am on July 5th when you get whisked away just in time for the end of the world.

Wear OS sucks. But I enjoyed working on the watchface. And I wanted some exercise with Svelte. So here it is!!

I'm going to refine this a bit as time goes on. And I'm sure there's some subtle (or not so subtle) bugs in there.

There's no SECRETS yet (or are there?), but I hope to add something fun to help usher in The Moment when it comes. ;)

Source, of course, is available over here:


Behold your vector nightmare, Amiga fans!

2021-03-06 - Reading time: 6 minutes

Kickstart1_3[3].pngOver on Hacker News there was an entry about the "awful" Amiga Kickstart 1.x icon and why it looked the way it did. This led to a link over on Stack Overflow where it was revealed that this was graphic actually drawn in a vector style, as opposed to raster pixels.

They also provided the actual bytes used to render the graphic. I thought it'd be fun to write a little parser to render these bytes.

And so did several other people, apparently, as I'm discovering now. :|

Anyway, here's my humble Codepen. And if that goes down, the original code is below the embed. 😎

(I skipped flood fill because I used two.js for this, and didn't realize until it was too late that I'd chosen poorly. Easy enough to swap out the graphics library, but it's time to move on.)

See the Pen Amiga Kickstart vector parser by Toby D (@Fortyseven) on CodePen.

Inspired by:


const floppy = [
    0xFF, 0x01, 0x23, 0x0B, 0x3A, 0x0B, 0x3A, 0x21, 0x71, 0x21, 0x71, 0x0B, 0x7D, 0x0B, 0x88,
    0x16, 0x88, 0x5E, 0x7F, 0x5E, 0x7F, 0x38, 0x40, 0x38, 0x3E, 0x36, 0x35, 0x36, 0x34, 0x38,
    0x2D, 0x38, 0x2D, 0x41, 0x23, 0x48, 0x23, 0x0B, 0xFE, 0x02, 0x25, 0x45, 0xFF, 0x01, 0x21,
    0x48, 0x21, 0x0A, 0x7E, 0x0A, 0x8A, 0x16, 0x8A, 0x5F, 0x56, 0x5F, 0x56, 0x64, 0x52, 0x6C,
    0x4E, 0x71, 0x4A, 0x74, 0x44, 0x7D, 0x3C, 0x81, 0x3C, 0x8C, 0x0A, 0x8C, 0x0A, 0x6D, 0x09,
    0x6D, 0x09, 0x51, 0x0D, 0x4B, 0x14, 0x45, 0x15, 0x41, 0x19, 0x3A, 0x1E, 0x37, 0x21, 0x36,
    0x21, 0x36, 0x1E, 0x38, 0x1A, 0x3A, 0x16, 0x41, 0x15, 0x45, 0x0E, 0x4B, 0x0A, 0x51, 0x0A,
    0x6C, 0x0B, 0x6D, 0x0B, 0x8B, 0x28, 0x8B, 0x28, 0x76, 0x30, 0x76, 0x34, 0x72, 0x34, 0x5F,
    0x32, 0x5C, 0x32, 0x52, 0x41, 0x45, 0x41, 0x39, 0x3E, 0x37, 0x3B, 0x37, 0x3E, 0x3A, 0x3E,
    0x41, 0x3D, 0x42, 0x36, 0x42, 0x33, 0x3F, 0x2A, 0x46, 0x1E, 0x4C, 0x12, 0x55, 0x12, 0x54,
    0x1E, 0x4B, 0x1A, 0x4A, 0x17, 0x47, 0x1A, 0x49, 0x1E, 0x4A, 0x21, 0x48, 0xFF, 0x01, 0x32,
    0x3D, 0x34, 0x36, 0x3C, 0x37, 0x3D, 0x3A, 0x3D, 0x41, 0x36, 0x41, 0x32, 0x3D, 0xFF, 0x01,
    0x33, 0x5C, 0x33, 0x52, 0x42, 0x45, 0x42, 0x39, 0x7D, 0x39, 0x7D, 0x5E, 0x34, 0x5E, 0x33,
    0x5A, 0xFF, 0x01, 0x3C, 0x0B, 0x6F, 0x0B, 0x6F, 0x20, 0x3C, 0x20, 0x3C, 0x0B, 0xFF, 0x01,
    0x60, 0x0E, 0x6B, 0x0E, 0x6B, 0x1C, 0x60, 0x1C, 0x60, 0x0E, 0xFE, 0x03, 0x3E, 0x1F, 0xFF,
    0x01, 0x62, 0x0F, 0x69, 0x0F, 0x69, 0x1B, 0x62, 0x1B, 0x62, 0x0F, 0xFE, 0x02, 0x63, 0x1A,
    0xFF, 0x01, 0x2F, 0x39, 0x32, 0x39, 0x32, 0x3B, 0x2F, 0x3F, 0x2F, 0x39, 0xFF, 0x01, 0x29,
    0x8B, 0x29, 0x77, 0x30, 0x77, 0x35, 0x72, 0x35, 0x69, 0x39, 0x6B, 0x41, 0x6B, 0x41, 0x6D,
    0x45, 0x72, 0x49, 0x72, 0x49, 0x74, 0x43, 0x7D, 0x3B, 0x80, 0x3B, 0x8B, 0x29, 0x8B, 0xFF,
    0x01, 0x35, 0x5F, 0x35, 0x64, 0x3A, 0x61, 0x35, 0x5F, 0xFF, 0x01, 0x39, 0x62, 0x35, 0x64,
    0x35, 0x5F, 0x4A, 0x5F, 0x40, 0x69, 0x3F, 0x69, 0x41, 0x67, 0x3C, 0x62, 0x39, 0x62, 0xFF,
    0x01, 0x4E, 0x5F, 0x55, 0x5F, 0x55, 0x64, 0x51, 0x6C, 0x4E, 0x70, 0x49, 0x71, 0x46, 0x71,
    0x43, 0x6D, 0x43, 0x6A, 0x4E, 0x5F, 0xFF, 0x01, 0x44, 0x6A, 0x44, 0x6D, 0x46, 0x70, 0x48,
    0x70, 0x4C, 0x6F, 0x4D, 0x6C, 0x49, 0x69, 0x44, 0x6A, 0xFF, 0x01, 0x36, 0x68, 0x3E, 0x6A,
    0x40, 0x67, 0x3C, 0x63, 0x39, 0x63, 0x36, 0x65, 0x36, 0x68, 0xFF, 0x01, 0x7E, 0x0B, 0x89,
    0x16, 0x89, 0x5E, 0xFE, 0x01, 0x22, 0x0B, 0xFE, 0x01, 0x3B, 0x0B, 0xFE, 0x01, 0x61, 0x0F,
    0xFE, 0x01, 0x6A, 0x1B, 0xFE, 0x01, 0x70, 0x0F, 0xFE, 0x01, 0x7E, 0x5E, 0xFE, 0x01, 0x4B,
    0x60, 0xFE, 0x01, 0x2E, 0x39, 0xFF, 0xFF

class AmigaVectParser {
    constructor(bytes, elem) {
        this.palette = ["#FFFFFF", "#000000", "#7777CC", "#BBBBBB"];
        this.offset = [0,0];
        this.prevOffset = [0,0];
        this.curColor = 0;
        this.isDrawing = false;
        this.buffer = bytes || [0xff, 0xff];
        this.done = false;

        this.two = new Two({ width: 640, height: 400 }).appendTo(elem);

    doCmd(cmd_pair) {
        if (cmd_pair[0] === 0xff) {
            this.isDrawing = false;
            if (cmd_pair[1] === 0xff) {
                // cmd_done
                this.done = true;
            } else {
                // cmd_colorSet
                this.curColor = cmd_pair[1];
        } else if (cmd_pair[0] === 0xfe) {
            // cmd_floodFill
            this.isDrawing = false;
            this.pointer += 2; //TODO FLOOD FILL
        if (!this.isDrawing) {
            // first coordinate in a poly-line
            this.prevOffset[0] = cmd_pair[0];
            this.prevOffset[1] = cmd_pair[1];
            this.isDrawing = true;
        } else {
            // continuing the poly-line
            this.offset[0] = cmd_pair[0];
            this.offset[1] = cmd_pair[1];

            let line = this.two.makeLine(
                this.prevOffset[0] * 2,
                this.prevOffset[1] * 2, // doubling up X/Y to make it easier to see at 640x400
                this.offset[0] * 2,
                this.offset[1] * 2

            line.stroke = this.palette[this.curColor];
            line.linewidth = 1;

            this.prevOffset[0] = this.offset[0];
            this.prevOffset[1] = this.offset[1];

    draw() {
        let cmd = [0, 0];
        let pointer = 0;
        this.done = false;

        while (!this.done) {
            cmd[0] = this.buffer[pointer++];
            cmd[1] = this.buffer[pointer++];

renderer = new AmigaVectParser(floppy, document.getElementById("draw-shapes"));

BYTE's Audio Cassette Standards Symposium

2020-12-20 - Reading time: 11 minutes

(This is a mirror from [], which itself is a mirror from BYTE Magazine. Minor formatting changes have been introduced.)

BYTE's Audio Cassette Standards Symposium

Written by Manfred and Virgina Peschke
BYTE, Feb 1976, Pages 72 and 73

BYTE Magazine sponsored a symposium on November 7 and 8, 1975 in Kansas City MO regarding the interchange of data on inexpensive consumer quality audio cassette drives.

These drives may be used as one of the mass storage devices in the first generation of personal computers, and will retain importance for some time to come as a means of interchange of software between computer enthusiasts who purchase products of the small systems industry.

In order to promote the growth of the industry, BYTE sought to achieve an industry standard on audio cassette data interchange through a working conference.

We extend our greatest appreciation to the 18 people who worked very hard until late Friday night and Saturday morning to discuss the multitude of problems and solutions associated with digital recording on auto cassettes. The names of the participants are listed in Table 1.

In spite of the short time available, the participants were able to draft a set of provisional standards which seems to promise great reliability and is rather inexpensive to implement; implementations may be entirely in hardware, or may require a mix of software and some minimal hardware.

Considerations were given to the problems of speed variation among recorders and playback equipment, start and stop delays, recording density (or speed) versus reliability, and recording frequencies to avoid interference with the telephone network in case some users plan to transmit the tones of the cassette over the phone lines.

On Saturday afternoon, Mr. Felsenstein and Mr. Mauch volunteered to write up the consensus among the participants as to a provisional standard which has been reproduced below.

Provisional Audio Cassette Data Interchange Standard
The consensus among the participants of the audio cassette standards symposium at Kansas City MO sponsored by BYTE Magazine is as follows:

  • The proposed standard centers around the use of a frequency shift modulation method from which serial clock data can be extracted at rates of up to 300 baud. The system is intended to be used with low to medium cost cassette recorders incorporating electrical stop and start capability which may be operated under program control.
  • The technique proposed provides for long and short term tape speed variation, limitations in bandwidth due to effects such as tape misalignment, and the necessity to retain low cost and low complexity of the hardware. The technique allows for potential operation at higher tape speed than the nominal 1.875 inch/s (4.75 cm/s).
  • A mark (logical one) bit consists of eight cycles at a frequency of 2400 Hz.
  • A space (logical zero) bit consists of four cycles at a frequency of 1200 Hz.
  • A recorded character consists of a space as a start bit, eight data bits, and two or more marks as stop bits.
  • The interval between characters consists of an unspecified amount of time at the mark frequency. In this respect the data format is similar to that of asynchronous data communication.
  • The eight data bits are organized least significant bit first, most significant bit last, and followed (optionally) by a parity bit. The total number of significant bits and the parity bit cannot exceed 8.
  • Where less than eight data bits are used, the unused bits (following the optional parity bit) at the end of the character are mark bits (2400 Hz).
  • Data will be organized in blocks of arbitrary and optionally variable length, preceded by a minimum of five seconds of marks. To avoid errors due to splice and wrinkle problems common at the beginning of tape, the beginning of the first data block will occur no sooner than 30 seconds from the beginning of clear leader.
  • The contents of the data block are not specified.
  • The data block ends after the stop bits of the final character.
  • Bit clocking information may be extracted from the recorded waveform, which is always an integer multiple of the bit rate, regardless of tape speed. This permits the recovery and retiming of data by means of a UART, which requires a clock of sixteen times the bit rate, although other simple circuitry may be used.
  • A reliable bandwidth of 3000 Hz was assumed in choosing mark and space frequencies due to the head misalignment expected between various cassette recorders. The recording technique is a redundant form of Manchester or bifrequency code which has a long history of reliability in the computer industry. In its present form it was proposed by three independent manufacturers at the conference. One cited reliability rates of one error in 10**7 characters for 200 passes.

Table 1: Participants at Audio Cassette Symposium.

Ray Borrill 1218 Prairie Dr, Bloomington IN
Hal Chamberlin The Computer Hobbyist, P 0 Box 5985, Raleigh NC 27607
Tom Durston MITS, 6328 Linn NE, Albuquerque NM
Lee Felsenstein LGC Engineering, 1807 Delaware St, Berkeley CA 94703
Joe Frappier Mikra-D, 32 Maple St, Bellingham MA
Bill Gates MITS
Gary Kay Southwest Technical Products Corp, 219 W Rhapsody, San Antonio TX 78216
Bob Marsh Processor Technology, 2465 Fourth St, Berkeley CA 94710
Harold A Mauch Pronetics, 4021 Windsor, Garland TX 75042
Bob Nelson PCM, San Ramon CA
George Perrine HAL Communications Corp, Box 365, Urbana IL 61801
Ed Roberts MITS
Richard Smith The Computer Hobbyist, P 0 Box 5882, Raleigh NC 27607
Les Solomon Popular Electronics, 1 Park Av, New York NY 10016
Michael Stolowitz Godbout Electronics, Box 2355, Oakland Airport CA 94614
Paul Tucker HAL Communications Corp
Mike Wise Sphere, 791 S 500 W, Bountiful UT 84010
Bob Zaller MITS