# Recreating the ZX Spectrum loader with Web APIs

This talk is about using new technology to replicate an old, reasonably useless, technology: replicating the ZX Spectrum tape loader audio and visuals (but without the tape…), and sharing what I learned along the way.

October 06, 2017

## Transcript

3. None
4. None
5. None

7. None
8. None
9. None
10. None
11. None
12. None
13. None
14. None
15. None

20. None
21. None
22. None
23. None

27. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave
28. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses
29. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T
30. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T * binary 1 = 1710T

32. None
33. None

36. ### const T = 1/3500000; const SAMPLE_RATE = 44100; // 44.1Mhz

const ONE = 1710 * 2; // 2 = HIGH + LOW const asHz = pulse => 1 / (T * pulse); const toRadian = hz => hz * Math.PI * 2; function generateSample(output) { const length = ONE * T * SAMPLE_RATE; for (let i = 0; i < length; i++) { const time = i / SAMPLE_RATE; const angle = time * toRadian(asHz(ONE)); // store a square wave output[i] = Math.sin(angle) < 0 ? -1 : 1; } }
37. ### 000100110000000000000000000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011 011101000000000011111111001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101 001111000010000000010001011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011 010101000010000000110100000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000 100100001001000010010000100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000 100100001001000010010000100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000 100100001001000010010000100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110 011001010110001101110100001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011 010111000011111000000010110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000 110011010011110000100000110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110

000010100000110010010000100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100 000011001001000010010000100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100 100100001001000010010000100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110 011111100011110000011000000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000 000000000000000000000000100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110 110000110000000100001100000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011 010011110100111001001110010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000 100100000001011000001011000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000 000101100000110100001100100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110 000011110000110010010000100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000 000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111 001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001 011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100 000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000 100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000 100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000 100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100 001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010 110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000 110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000 100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000 100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000 100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000 000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000 100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100 000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110 010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011 000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100 100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000 100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111 011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011 001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001 010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001 000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000 000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110 000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000 001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100001101000010000000100000 011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010110011010000000100010110 000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000110010010001011000000110 000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000100100001001000010010000 100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000100100001001000010010000 100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000100100001001000010010000 100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000000000001000010100010011 000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000100000001011101101110100 000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100000000001100110100111100 001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110010001010100001101010100 001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011000011001001000010010000 100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100100100001001000010010000 100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000100100001001000010010000 100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111011011100110111001100101 011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011001000100111101101011100 001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001010000000000000011001101 001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001000000010001011000001010 000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000000101100000110000001100 100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110000011100000110010010000 100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000001111000111111001111110
38. None

40. ### this.node = ctx.createScriptProcessor(bufferSize, 1, 1); this.node.onaudioprocess = audioProcessingEvent => {

const channel = 0; const inputBuffer = audioProcessingEvent.inputBuffer; const input = inputBuffer.getChannelData(channel); // then we'll read the values for own processing this.read(input, performance.now()); // copy the input directly across to the output const outputBuffer = audioProcessingEvent.outputBuffer; const output = outputBuffer.getChannelData(channel); inputBuffer.copyFromChannel(output, channel, channel); }; // constructor continues...

49. ### * Draw image into canvas * export canvas as blob

* File Reader API to read as binary string * convert char to binary
50. ### * Draw image into canvas * export canvas as blob

* File Reader API to read as binary string * convert char to binary * TURN binary INTO audio
51. ### function imageToBlob(img) { const c = document.createElement('canvas'); const ctx =

c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }
52. ### function imageToBlob(img) { const c = document.createElement('canvas'); const ctx =

c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }
53. ### function fileToBinary(blob) { return new Promise(resolve => { const reader

= new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }
54. ### function fileToBinary(blob) { return new Promise(resolve => { const reader

= new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }
55. ### function charToBinary(chr) { return char .charCodeAt(0) // R = 82

.toString(2) // 82 = 1010010 .padStart(8, '0'); // 1010010 = 01010010 }
56. None

58. None

62. None

pulse
67. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len
68. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1
69. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits
70. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits * <Repeat>
71. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits * <Repeat> * make image from bytes
72. ### export function loadEdge1(buffer) { if (!buffer.length) { return null; }

let last = null; let point = buffer.shift(); pulseBuffer.push(point); do { // search for when the buffer point crosses the zero threshold if (last !== null) { // important: when we hit an edge, the data doesn't include the edge if (edge(point, last)) { // create a new array and return that instead const res = Array.from(pulseBuffer); // reset the buffer pulseBuffer = []; return res; } } pulseBuffer.push(point); last = point; } while ((point = buffer.shift())); return null; // no edge found }

74. ### PULSE = 1 / sample rate * (HI count) BIT

= PULSE === 855 ? 0 : 1;

76. None
77. None
78. None
79. None
80. ### 4b FF 80 32 00 a1 42 02 BB 80

c3 20 d1 d9 ff 02

82. None
83. None
84. None
85. None

87. None

90. None