2

I would like to take an md5 hash of some content, and then generate a "curve" or a "spectrum" so to speak, of n points. That is, to plot let's say 5, 10, or 20 points on a line from 0 to 1, distributed in a way so that it's unique to the md5 hash (collisions don't much matter). Basically it would look like a atomic light emission spectrum.

enter image description here

enter image description here

These points (or lines in the spectra) are somehow generated based on the md5 hash provided, and the n provided saying how many lines you want.

So it would be like:

function generateSpecrum(md5, n) { return [ ... ] }

By default it could just return the values from between 0 and 1, but maybe you give it a start and end value from which to generate the range.

Wondering how this could be done, in pseudocode or in JS.

There will be however many possibilities of a standard md5 hash. I would just do this:

var crypto = require('crypto')
var data = 'foo'
crypto.createHash('md5').update(data).digest('hex')
// acbd18db4cc2f85cedef654fccc4a4d8

So a 32-byte string. In my case it doesn't need to produce globally unique values, there can be some collisions, but if there was a way for it to produce a variety of spectra from different md5 inputs, that would be cool.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
Lance
  • 75,200
  • 93
  • 289
  • 503
  • I think I see what you're trying to do here. md5 hash is 128 bits or 16 bytes. It would be nice to divide it into groups of 8 bit triples to create RGB color lines, but 16 is not divisible by 3. So just reuse two of the 8 bit fields for the last color triple. This will give you 6 lines. – President James K. Polk Jun 11 '19 at 12:08
  • You can of course create more lines with less color resolution by choosing smaller bit fields. For example, by using 4 bits field you could have 11 lines, and with 3 bits fields you could have 15 lines. – President James K. Polk Jun 11 '19 at 12:16
  • 1
    @JamesKPolk Or you could do greyscale with 8 bits per line (0 being black, and 255 being white). Or map the 256 values to a [halftone palette](https://en.wikipedia.org/wiki/Halftone). – Jim Mischel Jun 11 '19 at 17:29
  • Come to think of it, you could use 5-bit fields. That would give you 25 different fields and 32 different colors that you can map to a 32-color half tone palette. You'd have three bits left over. I suppose you could make three of the fields 6 bits each. Or have a 26th field that can only display 8 different colors. – Jim Mischel Jun 12 '19 at 04:32
  • Why not use the Hash as a Seed for random and generate whatever using pseudo random generator? Like get number fo wavelengths and then the wavelenghts .... compute the RGB of wavelengths from [RGB values of visible spectrum](https://stackoverflow.com/a/22681410/2521214) or from poor HSV approximation instead. ... so all boils down to 1+n Random calls – Spektre Jun 12 '19 at 11:46
  • If you have a 32 byte print, you have 128 discrete bits to work with. That's _more_ than plenty (but for increased spectrum fidelity, use a sha print instead of md5, since that gives you 160 bits at minumum, with sha-256 being fast to compute these days, and giving oodles of bits). Then just treat each bit as "emits" vs "doesn't emit", and done. See answer. – Mike 'Pomax' Kamermans Jun 13 '19 at 00:28

1 Answers1

3

Let's ignore the part where the string data is an md5 print and instead focus on how to do this for arbitrary length hexadecimal strings, so we can use any digest we like (from CRC32 to SHA-512):

  1. start with a hue gradient background (we can do this in CSS),
  2. turn the string into a bit print (built into JS), and
  3. black out any region that corresponds to a zero bit.

As a runnable snippet:

function hexstr2bin(stringinput) {
  // let's not be constrained by JS integer precision,
  // which is only good for 53 bits. Technically we don't
  // care what the "numbers" are here, we just want the
  // ones and zeros that the numbers turn into.
  return stringinput.split('').map(c => (
    parseInt(c, 16).toString(2).padStart(4,'0')
  )).join('');
}

function renderSpectrum(stringinput) {
  let cvs = document.createElement('canvas');
  let bits = Array.from(hexstr2bin(stringinput));

  cvs.width = bits.length;
  cvs.height = 1;
  ctx = cvs.getContext('2d');
  ctx.strokeStyle = 'black';
 
  bits.forEach( (bit,i) => {
    if (bit === "0") {
      ctx.moveTo(i,0);
      ctx.lineTo(i,1);
      ctx.stroke();
    }
  });

  document.body.appendChild(cvs);
};

renderSpectrum("acbd18db4fccc4a4d8");
renderSpectrum("c5887c91d0002f2a869a4b0772827701");
renderSpectrum("06956ff032d78e090d0d292aa9d8e7143ab08cf1ed444944529f79a4f937306a");
canvas {
  width: 100%;
  height: 40px;
  background: linear-gradient(
    to right,
    violet, blue, cyan, green, yellow, orange, red
  );
}

And stretching the canvas to 100% width means you get blurring for free. Bonus!

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153