There was a blog post about oscilloscope art from waveforms generated in Javascript recently. I made a wrencher.

You can display it on your own scope by hooking the left and right audio outputs from your soundcard (or smartphone, which is how I generated the image above - and photographed it at the same time!) to two oscilloscope inputs in X-Y mode. Here's a link to the jsfiddle which you can run directly.
The original idea and code to create audio and play it in JS come from Neil Fraser. You can find it on his blog.
I grabbed a wrencher bitmap I had around, and pasted it into xfig, an old-school vector drawing program that I can't unlearn, no matter how hard I try. I traced the outline of the wrencher parts with polylines in xfig, and saved the result. The fig file format is ASCII, so I was able to just cut and paste the points directly into JS. All the scaling and translation is done in the JS code so I could just use the points directly. You can probably paste pretty much anything in there, and it will do something, maybe.
EDIT: @Thomas asked about a y-t view of the waveforms and the spectra. Here they are (not very instructive, it turns out):


I don't know how long jsfiddles live, so here's the code, too. Save it with an html extension, then open in your browser.
<html>
<body>
<input type="button" value="logo" onclick="had_logo()" />
</body>
<script>
function had_logo() {
var points = [
[4752, 5407, 4757, 5131, 4832, 4847, 4859, 4808, 4974, 4617, 5139, 4444,
5214, 4390, 5409, 4266, 5684, 4200, 5884, 4222, 6141, 4297, 6292, 4386,
6417, 4510, 6528, 4652, 6634, 4816, 6710, 5052, 6736, 5300, 6705, 5584,
6656, 5735, 6550, 5948, 6479, 6050, 6369, 6151, 6301, 6210, 6341, 6285,
6301, 6436, 6150, 6507, 6031, 6463, 5955, 6379, 5964, 6285, 5920, 6276,
5920, 6374, 5866, 6445, 5769, 6503, 5627, 6467, 5556, 6387, 5547, 6281,
5529, 6276, 5511, 6387, 5445, 6472, 5338, 6507, 5232, 6467, 5165, 6365,
5161, 6237, 5181, 6203, 5171, 6195, 5118, 6139, 5121, 6143, 4961, 5979,
4868, 5806, 4823, 5722
],
[5070, 6205, 4721, 6525, 4717, 6738, 4633, 6924, 4455, 7080, 4269, 7133,
4100, 7133, 4038, 7115, 4384, 6809, 4025, 6410, 3674, 6711, 3661, 6560,
3723, 6352, 3834, 6201, 3985, 6121, 4193, 6077, 4357, 6094, 4766, 5762
],
[3683, 3778, 3683, 3769, 3670, 3893, 3696, 4071, 3789, 4217, 3923, 4346,
4136, 4408, 4357, 4395, 4791, 4761, 4797, 4754, 4859, 4648, 4926, 4532,
5123, 4342, 5134, 4333, 5098, 4301, 4717, 3951, 4717, 3898, 4721, 3782,
4677, 3658, 4593, 3525, 4477, 3423, 4357, 3370, 4167, 3356, 4056, 3374,
4380, 3676, 4020, 4075, 3683, 3778],
[6701, 4737, 6607, 4612, 6536, 4488, 6368, 4346, 6346, 4336, 6763, 3960,
6776, 3756, 6838, 3600, 6954, 3463, 7140, 3374, 7286, 3356, 7428, 3370,
7105, 3671, 7464, 4080, 7806, 3782, 7819, 3862, 7815, 4018, 7699, 4235,
7539, 4359, 7375, 4417, 7247, 4421, 7118, 4404, 6713, 4750, 6701, 4737
],
[6088, 5043, 6257, 5034, 6377, 5123, 6448, 5225, 6452, 5367, 6430, 5500,
6355, 5566, 6284, 5606, 6257, 5509, 6159, 5438, 5999, 5358, 5928, 5273,
5920, 5149, 6017, 5078, 6070, 5047, 6088, 5043
],
[5737, 5591, 5720, 5611, 5671, 5731, 5658, 5850, 5662, 5926, 5711, 5850,
5755, 5802, 5791, 5864, 5818, 5930, 5840, 5864, 5822, 5731, 5782, 5620,
5756, 5589, 5737, 5591
],
[6439, 6205, 6559, 6068, 6674, 5890, 6727, 5753, 6734, 5744, 7140, 6094,
7286, 6072, 7460, 6090, 7615, 6188, 7739, 6316, 7819, 6503, 7815, 6698,
7477, 6414, 7100, 6809, 7451, 7115, 7344, 7146, 7087, 7115, 6896, 6973,
6763, 6738, 6767, 6521, 6430, 6218, 6439, 6205
],
[5321, 5022, 5431, 5038, 5542, 5114, 5578, 5229, 5542, 5327, 5391, 5416,
5276, 5455, 5232, 5504, 5227, 5602, 5152, 5602, 5041, 5469, 5019, 5309,
5063, 5167, 5174, 5065, 5303, 5025, 5321, 5022
]
];
min = 10000;
max = 0;
for (var i = 0; i < points.length; i++) {
segment = points[i];
for (var j = 0; j < segment.length; j++) {
p = segment[j];
min = Math.min(min, p);
max = Math.max(max, p);
}
}
left = [];
right = [];
left.push(128);
right.push(128);
for (var k = 0; k < points.length; k++) {
xy = points[k];
oldx = xy[0];
oldy = xy[1];
xy.push(oldx);
xy.push(oldy);
while (xy.length) {
x = xy.shift();
y = xy.shift();
d = Math.sqrt((oldx - x) * (oldx - x) + (oldy - y) * (oldy - y));
n_pts = Math.floor(d /50) + 1;
console.log(n_pts);
for (var i = 0; i < n_pts; i++) {
xp = oldx + (x - oldx) * (1.0*i / n_pts);
yp = oldy + (y - oldy) * (1.0*i / n_pts);
left.push(Math.max(Math.min(255 * (xp - min) / (max - min), 255), 0));
right.push(Math.max(Math.min(255 - 255 * (yp - min) / (max - min), 255), 0));
}
oldx = x;
oldy = y;
}
}
left.push(128);
right.push(128);
left = left.concat(left);
right = right.concat(right);
left = left.concat(left);
right = right.concat(right);
left = left.concat(left);
right = right.concat(right);
left = left.concat(left);
right = right.concat(right);
left = left.concat(left);
right = right.concat(right);
left = left.concat(left);
right = right.concat(right);
console.log(left.length);
var wav = makeWav(left, right);
var audio = new Audio();
audio.src = 'data:audio/x-wav;base64,' + btoa(wav);
audio.loop = true;
audio.play();
}
// from https://neil.fraser.name/news/2018/01/25/
function makeWav(left, right) {
// Return a stereo WAV file built from the provided data arrays.
var min = Math.min(left.length, right.length);
var SubChunk2Size = min * 2;
// RIFF chunk descriptor.
var file = 'RIFF';
file += numToLong(36 + SubChunk2Size); // ChunkSize
file += 'WAVE';
// The 'fmt ' sub-chunk.
file += 'fmt ';
file += numToLong(16); // Subchunk1Size
file += numToShort(1); // AudioFormat
file += numToShort(2); // NumChannels
file += numToLong(48000); // SampleRate
file += numToLong(48000 * 2); // ByteRate
file += numToShort(2); // BlockAlign
file += numToShort(8); // BitsPerSample
// The 'data' sub-chunk.
file += 'data';
file += numToLong(SubChunk2Size);
for (var i = 0; i < min; i++) {
file += numToChar(left[i]) + numToChar(right[i]);
}
return file;
}
function numToChar(num) {
// num is 0 - 255
return String.fromCharCode(num);
}
function numToShort(num) {
// num is 0 - 65536
var b0 = num % 256; // low
var b1 = (num - b0) / 256; // high
return String.fromCharCode(b0) + String.fromCharCode(b1);
}
function numToLong(num) {
// num is 0 - 4.2billion
var b0 = num % 256;
num = (num - b0) / 256;
var b1 = num % 256;
num = (num - b1) / 256;
var b2 = num % 256;
num = (num - b2) / 256;
var b3 = num;
return String.fromCharCode(b0) + String.fromCharCode(b1) +
String.fromCharCode(b2) + String.fromCharCode(b3);
}
</script>
</html>
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
Hi. Is it possible to do this without Xfig? I am on a Mac. I found an online converter, so I drew a shape in Adobe Illustrator, exported it as a .png, and converted it into .fig online, then opened the file in the Mac text edit app, but I don't think it works that way, right?
Are you sure? yes | no
Thanks for adding the scope traces. The slew rate of the DAC and the audio outputs is pretty impressive (and the spectrum is quite mean). It should be possible to use an audio output as a MF transmitter.
Are you sure? yes | no
Well, you've got two channels, so producing I and Q is easy. You just need to mix with a carrier to transmit whatever modulation on whatever band you like. There are SDRs out there that do exactly that.
Some soundcards have input sampling frequencies up to 192kHz. I'm not sure if their outputs work the same way.
Are you sure? yes | no
I checked whether a 5" b/w TV can be turned into a X/Y oscilloscope (think Asteroids) but I only found 16kHz fixed rate audio "scopes". It shouldn't be that difficult ...
Are you sure? yes | no
In my college days (late 1980s), I converted a (13"?) b/w TV into a rough audio vectorscope - I just brought the deflection coil terminals back to a pair of RCA jacks mounted on the back panel. I would hook them in parallel with my stereo speakers for parties. It was a modest hit.
Are you sure? yes | no
"Modest hit" sounds familiar. The light show I made for the high school disco was nothing compared to the super-8 skin flicks someone projected on a bed linen from the back side.
Are you sure? yes | no
@Thomas some things are difficult to compete with.
Are you sure? yes | no
I'm impressed how much can be done with the 3.5mm audio jack right to my wrist. Two channel traces (Y1, Y2, X=t), and a the spectrum of one channel would be interesting.
Edit:
Ah, now I can hear it :-)
I had a look at Neil Fraser's experiments. Nice!
Are you sure? yes | no
I think this could be a new art medium: youtube videos whose audio makes some cool animation on your oscilloscope.
Are you sure? yes | no