Archiving photos on a ZX Spectrum

Archiving photos on a ZX Spectrum

Recently, I wrote a few words about Demis Hassabis, a computer scientist who won the Nobel Prize in Chemistry. With the upcoming release of a ZX Spectrum reproduction from Retro Games Ltd, I couldn’t help but reminisce about my early days in computing with a ZX Spectrum. As I sifted through the history of the ZX, I came across a photo of Demis with a ZX Spectrum—it turns out he started out on the same machine! Maybe there’s hope for me yet.

Demis Hassabis with his first computer, a ZX Spectrum 48K.
Demis Hassabis with his first computer, a ZX Spectrum 48K. (Source: Unknown! - Sorry)

I have a lot of photos and always seem to run out of storage space. Recently, while clearing out some old boxes, I came across a stack of 90-minute audio cassettes from my ZX Spectrum days. Inspired by a wave of nostalgia, I wondered, Could I use these tapes to store my photo collection? After all, they worked perfectly well for storing ZX Spectrum data back in the day—why not give it a shot?

How the ZX Spectrum Stored Data

As a child, loading data from a cassette felt like magic, and I couldn’t resist looking back to understand how this deceptively simple ‘magic’ really worked. The ZX Spectrum loaded games and data from cassette tapes by encoding binary data (0s and 1s) as audio signals. Here’s a closer look at the tape format and the clever mechanics behind how the ZX Spectrum stored data on tape:

Pilot Tone: To help the ZX Spectrum synchronize with the incoming data, a continuous high-frequency tone, or “pilot tone,” played for around 8063 pulses (a total duration of about 5 seconds) at 2400 Hz. This was followed by two sync pulses of 1200 Hz and 2400 Hz.

Data Encoding with Manchester Encoding: The data itself was stored using a method called Manchester encoding, named at the University of Manchester, where it was first developed. This encoding ensured clear transitions for each bit:

  • A 0 bit was represented by a cycle at 1200 Hz (a duration of 2083 microseconds).
  • A 1 bit was represented by a cycle at 2400 Hz (a duration of 1042 microseconds).

These frequencies and durations were a clever solution, ensuring that the ZX Spectrum could read and interpret each bit accurately, even with slight fluctuations in the cassette speed.

Modern Take: Backing Up a JPEG to Cassette Tape

With the basics of the ZX Spectrum tape format in mind, I set about seeing if I could save a modern JPEG photo using the same process. My approach was to write a Python script that would read an image file (or any binary file), encode it in the same way the ZX Spectrum would, and save it as a WAV file. I could then transfer this WAV file to a cassette tape for a true retro backup experience.

Encoding the Image

The first step was to open my image file in binary mode and read its contents as a sequence of bytes. Each byte of data in a file is composed of 8 bits—either 0s or 1s. My goal was to translate each of these bits into a corresponding sound wave, just as the Spectrum had done for games and programs.

To mimic the Spectrum’s loading process, I started with a pilot tone. This tone, a continuous 2400 Hz sound wave, was used by the Spectrum to synchronize its reading mechanism. I generated this tone as a sine wave and set it to play for a few seconds at the beginning of the audio file. This pilot tone would help establish a stable starting point for decoding the data later.

With the pilot tone in place, I moved on to the core of the encoding process: Manchester encoding. The idea behind Manchester encoding is simple yet effective. Each bit of data, whether a 0 or a 1, is represented by a specific waveform transition:

  • A 0 bit translates into a high-to-low transition (1200 Hz cycle).
  • A 1 bit is represented by a low-to-high transition (2400 Hz cycle).

This encoding scheme ensures that each bit is defined by a clear shift in frequency, making it easier to decode with accuracy. For each byte in the image, my script converted the 8 bits into these high-to-low and low-to-high transitions, building up a waveform that would represent the entire file. Finally, I combined this waveform with the pilot tone at the beginning and saved it all as a WAV file. This WAV file held the essence of the image, transformed into sound, ready for playback on a cassette deck.

Decoding the Image

With the encoded image saved as audio, the next challenge was to decode it back into a usable file. The script needed to play back the audio, interpret each waveform transition, and reconstruct the original binary data.

To begin decoding, I skipped the pilot tone at the start. Once I moved past this initial synchronization signal, I could focus on reading the actual data. For each bit, I analysed the waveform transitions in small time intervals. The transitions worked just as they did in encoding: a high-to-low transition signalled a 0, while a low-to-high transition represented a 1. By reading each bit in sequence, I converted the waveform back into a stream of 0s and 1s.

After decoding all the bits, the next step was to reconstruct each byte by grouping every 8 bits together. These bytes eventually recreated the entire binary content of the original image. With the data back in its original form, I saved it to a new file.

Theoretical Maximum Storage on a 90-Minute Cassette

This experiment got me thinking: how much data could I theoretically store on a 90-minute cassette tape using this method?

With the bit rate set to 1200 bits per second, each second of audio can store 1200 bits (either 0s or 1s). A 90-minute cassette gives us 10,800 seconds of total recording time if both sides are used.

Using this bit rate, the total number of bits that can be stored on a 90-minute cassette is:

Total Bits: Multiply the bit rate by the total recording time:

  • 1200 bits per second * 10,800 seconds = 12,960,000 bits.

Convert Bits to Bytes: Divide the total bits by 8 to get the storage capacity in bytes:

  • 12,960,000 bits / 8 = 1,620,000 bytes.

Convert Bytes to Kilobytes and Megabytes:

  • 1,620,000 bytes / 1024 = 1582.03 KB.
  • 1582.03 KB / 1024 = approximately 1.54 MB.

So, using Manchester encoding at 1200 bits per second, a 90-minute cassette could theoretically store around 1.54 MB of data. This capacity would almost be enough for a single 2MB photo, illustrating how limited tape storage is compared to modern digital storage options.

Reflections and Absurdity

Although I admit this solution cuts a few corners—and maybe in the next iteration I’ll try loading and saving directly to tape via the sound card, rather than through a WAV file—revisiting the ZX Spectrum’s storage format was both nostalgic and enlightening. It reminded me of just how much technology has advanced since the days of cassette tapes. Back then, storage was extremely limited, and cassette tapes offered a clever workaround for squeezing the most out of every byte. Today, we can store entire libraries on devices smaller than a tape’s spool. And yet, here I was, backing up a single photo onto a cassette—because why not?

So, could I back up my entire photo album this way? Not unless I was content with a couple of low-res photos per tape! The thought of dedicating a full 90-minute cassette to a single JPEG makes me smile—it’s a playful nod to where computing began for many of us and a reminder of just how far we’ve come. And, of course, waiting 90 minutes for each photo to load makes me cringe a little, but it also brings back fond memories of those long waits for games on the ZX Spectrum.

Not that the 48K Spectrum could have loaded or displayed the photo anyway! For a machine with only 48K of memory, using cassette tapes as storage made a lot of sense back then. Today, the limits of cassette storage feel amusingly quaint, but the cleverness of those early solutions still impresses me. And while the process was impractical, the nostalgia was worth every second.?

Full code here and?for those who just want to share the nostalgia, listen to the encoded file here (encoded to mp3 to save bandwidth).

要查看或添加评论,请登录

Chris Hughes的更多文章

社区洞察

其他会员也浏览了