Processing: How to Hide Messages in Static

Poltergeist was hands down the scariest movie I saw as a child. It hard coded my brain to associate static on television with sinister messages from the beyond. Now that I’m older,  I’ve come to the realization that static is a great place to hide all kinds of messages (thanks Poltergeist). And Processing is the obvious place to make that happen.

Step 1: Create a Message

Let’s start off with a message measuring 600 by 300 pixels. I used Gimp to save it as a PNG. Now, the trick to making this work is setting the text to specific shade of gray. That means your red, green, and blue values all need to be the same number between 0 and 255. Setting all of them to 0 would get you black and 255 would get you white. For the best results, stick with something in the middle. In the example below, I picked 100.

Now, go into Processing and save a sketch. Once that’s done, hit Control + K. That’s the folder where you’ll stash your own Poltergeist message.

Make sure the image is saved there or Processing will have a massive cow.

Now, to hide those letters in the snow, I’m going to have Processing chop up the image into tiny strips along the X axis and overlay that onto a bunch of randomized gray pixels. I’m going to have it skip every other line to make it more difficult to read.

Step 2: Set A Bunch of Variables

Ok, if we’re going to stuff every other line with randomized pixels, the height of the image is going to expand. It’ll double. That’s why we have to set the size of our Processing window at twice the Y value of the message (below it equals 600). The step value is set at 2 so we’ll skip every other line. If you want to go real crazy with this, you can set it to 3 and triple the height of the finished image. imgx and imgy are going to help us keep track of the X and Y coordinates on pixels that we’re adding to our finished product. So the beginning of the setup() block will read:

void setup(){
int totalx = 600;
int totaly = 600;
int step = 2;
int imgx = 0;
int imgy = 0;
int rand = 0;

Then we’ll use loadImage()  and loadPixels() to bring our message into play, and createImage() to make a brand new set of pixels to write information to. I’m calling that “Img.”

PImage Img = createImage(width,height,ARGB);

You may be wondering how we’re actually going to access all them pixels. In Processing, pixels in an image are stored in what is called a pixel array. Here’s the important thing to note about the pixel array. It’s doesn’t store information in X and Y coordinates—the color data for each pixel is stashed from left to right, and top to bottom (see below), like if you were reading a book. You can, however, use this formula to get the pixel’s position in that book using X and Y coordinates.

It’s weird to wrap your head around at first, but it works. Once you have the position within that array, you can write a grayscale value using this line:

int imgloc = imgx + imgy * width;
Img.pixels[imgloc] = color(GreyScaleNumber);

Step 3: Write Static into the New Pixel Array

You’ll need a nested for loop to run through each X and Y value. Just remember that we have to cut the height in half for the Y loop because we’re skipping every other line. Also, Processing’s random() function returns a float, so you’ll need to use int() to convert the number before plugging it into color().

for (int x = 0; x<width;x++) {
for (int y = 0; y <height/step; y++) {
int loc = x + y*width;
rand = int(random(0,255));
int imgloc = imgx + imgy * width;
Img.pixels[imgloc] = color(rand);
imgy = imgy + step;
}
imgy = 0;
imgx = imgx + 1;
}

Step 4: Write the Message into the Static

In a separate nested loop, we can use red(), green(), and blue() to get the values of each color in the Poltergeist message. If those equal 255, then we know the pixel is white, and we can change it to a randomized grey value. If they are anything else, we can use color() to assign that pixel from the Poltergeist message into the new image.

for (int x = 0; x <width; x++) {
for (int y = 0; y < height/step; y++) {
int loc = x+y*width;
float r = red(Poltergeist.pixels[loc]);
float g = green(Poltergeist.pixels[loc]);
float b = blue(Poltergeist.pixels[loc]);
if ((r==255) && (g==255) && (b==255)) {
//Randomize the r, g, b values
rand = int(random(0,255));
r = rand;
g = rand;
b = rand;
}
int imgloc = imgx + imgy * width;
Img.pixels[imgloc] = color(r,g,b);
imgy = imgy + step;
}
imgy = 0;
imgx = imgx + 1;
}

Step 5: Update the Pixels and Save the Message

The last few lines of the sketch push the new image into a PNG for easy reference. By default, that image will just appear in your Sketch folder after you run it. updatePixels() should go first, then image(), and last save().

Img.updatePixels();
image(Img, 0,0);
save("Message.png");

A complete copy of the sketch is available on gist.

Here’s what the end result looks like:

Step 6: Ummmmmmm. How Do I Get It Out Again?

Yeah, good question. After it’s a Poltergeisted, you’ll probably want to be able to pull it back out again.

There’s a different sketch for that.

All it requires is that you have the message loaded in the sketch folder (below it’s named “Message.png”). Then, see where it says if((r==100) && (g==100)&&(b==100))? That’s where you plug your RGB values. The if statement works like a sifter after that, preserving any value that matches R: 100, G:100, B:100 as black, and everything else gets wiped out as white.

void setup() {
size(600,600);
PImage Message = createImage(width,height,ARGB);
for (int x = 0; x<width;x++) {
for (int y = 0; y <height; y++) {
int loc = x + y*width;
float r = red(Garbled.pixels[loc]);
float g = green(Garbled.pixels[loc]);
float b = blue(Garbled.pixels[loc]);
if((r==100) && (g==100) && (b==100)) {
Message.pixels[loc] = color(0);
} else {
Message.pixels[loc] = color(255);
}
}
}
Message.updatePixels();
image(Message,0,0);
}
void draw() {
}

And behold. All the pixels at greyscale value 100 without the underlying static.

There’s a gist for that one too.