Today were going to look at assembling Frankenstein… I mean Mr. Good Bot.

By the end you will have the code needed to run Mr. Good Bot in his most basic capacity.

Before we proceed though, I caution all braves souls who dare venture further, this post will be frightfully technical and you need to be prepared for the coding horrors that lay ahead!

If your stomach turns at the sight of raw code (i.e. most sane folks) then I would suggest you keep right at the fork and head towards A Spooky Real Neat Blog Award for a more engaging and seasonally festive experience.

Though before you go, if you just want the featured image for this post, here’s the wallpaper

Random Assembly Required 1920 by 1080 Wallpaper
Random Assembly Required 1920 by 1080 Wallpaper

Otherwise, here are the other posts in this series if you need to catch up:

The Anatomy of a Good Bot

Mr. Good Bot uses HTML & CSS as the UI/Front End and a little JavaScript to request an updated image frame from the server.

This methodology allows for a simple implementation that I can make accessible on my air gaped home intranet to any authorized device with an IP Address and a browser.

Meaning Xavier can use a “smart device” (laptop, tablet, cellphone, watch, etc…) to keep his robot buddy with him while he’s running around playing.

It’s also possible to modify Mr. Good Bot slightly to accommodate multiple concurrent users.

Meaning Mr. Good Bot could even serve as my families own “smart home” digital assistant and we wouldn’t have to worry about unscrupulous 3rd parties monitoring everything we do or say!

All digital assist bots monitor you for “Learning & Quality Assurance”, look it up!

A Good Bot should be like a pet, a trusted and beloved family member, not a spy for BNN!

As it is (NOT A JOKE) Big Neural Network can and does monitor you at “their discretion”  through your “virtual assistant” with the stated intent at best “to sell you more shit” (paraphrasing)!

But… Let’s not turn this into another rant that gets me blacklisted again. 😛

In any case, because the interface is basically just a webpage Mr. Good Bot has access to all the functionality a web browser offers with the computation of a server, all over my LAN. 🙂

This opens up the possibility to do just about anything we want without relying on BNN tech.

In addition to the front end code are the “skin” images which need not be a face and I included an example called Fireflies on GitHub that shows how this can work with something that isn’t a face but we’ll talk about that more in a minute.

The HTML & CSS are the User Interface (UI) and are very simple.

We implemented them in the Front End post though we didn’t discuss the CSS very much.

Mostly because the CSS is extremely simple and just sets the background to black and pulls the “Skin” drop-down selector to the right and sets page overflow to hidden.

Additionally, as part of the front end I added some JavaScript to perform AJAX calls to a PHP script called Face.php which assembles the images into a complete face and refreshes Mr. Good Bots image in the browser.

Face.php

Here’s the code that assembles the animation frame images.

<?php
// Example Use (Set skin & generate a face):
/*
	http://localhost/MrGoodBot/Face.php?skin=Default
	http://localhost/MrGoodBot/Face.php?skin=Nucleus
	http://localhost/MrGoodBot/Face.php?skin=Pixel
	http://localhost/MrGoodBot/Face.php?skin=Pumpkin
*/

// $_GET || $_POST skin
if(isset($_REQUEST['skin'])){
	$skin = $_REQUEST['skin'];
}
else{
    $skin = 'Default';
}

$path = __DIR__ . DIRECTORY_SEPARATOR . 'skins' . DIRECTORY_SEPARATOR . $skin . DIRECTORY_SEPARATOR;


// Find The Skin Template Images
$eyes = glob($path . "eyes_*.png");
$noses = glob($path . "nose_*.png");
$mouths = glob($path . "mouth_*.png");
$eyebrows = glob($path . "eyebrows_*.png");


// Select A Random Face
$eye = array_rand($eyes, 1);
$eyebrow = array_rand($eyebrows, 1);
$nose = array_rand($noses, 1);
$mouth = array_rand($mouths, 1);
$face = $path . 'face.png';


// Load Selected Part Images
$face = imagecreatefrompng($face); // Load Face
$eyes = imagecreatefrompng($eyes[$eye]);  // Load Eyes
$eyebrow = imagecreatefrompng($eyebrows[$eyebrow]); // Load Eyebrows
$nose = imagecreatefrompng($noses[$nose]);  // Load Nose
$mouth = imagecreatefrompng($mouths[$mouth]); // Load Mouth


// Enable Transparency 
imagealphablending($face, true);
imagesavealpha($face, true);


// What size is the image?
$width = imagesx($face);
$height = imagesy($face);


// Merge Parts of face image resources together
imagecopy($face, $eyes,0, 0, 0, 0, $width, $height);
imagecopy($face, $eyebrow,0, 0, 0, 0, $width, $height);
imagecopy($face, $nose,0, 0, 0, 0, $width, $height);
imagecopy($face, $mouth,0, 0, 0, 0, $width, $height);


// Save New Face Image in the Root Skins DIR
imagepng($face, __DIR__ . DIRECTORY_SEPARATOR . 'skins' . DIRECTORY_SEPARATOR .'current_face.png');


#FreeTheMemory!!!
imagedestroy($face);
imagedestroy($eyes);
imagedestroy($eyebrow);
imagedestroy($nose);
imagedestroy($mouth);

Here’s what happens:

  1. Face.php sets the variable $skin to default if you don’t specify a value.
  2. It creates a variable called $path to the skin assets directory.
  3. It uses glob() to find the face images based on these patterns:
    • eyes_*.png
    • nose_*.png
    • mouth_*.png
    • eyebrows_*.png
  4. Randomly select one of each of image parts.
  5. Declare $face to point to the “hard coded” face.png.
  6. Load all the selected images into memory using variables named after each part ($eyes, $nose, etc.).
  7. Enable transparency on the $face image because we need to preserve the transparency of the layers. Without this the transparent pixels would become the background color and whatever layer is merged last would obscure all other layers.
  8. Declare $width and $height to be the size of the $face image.
  9. Merge the image layers in the order specified.
  10. Save the new face animation frame as current_face.png in the root skins folder.
  11. #FreeTheMemory!!!

With minor changes to the code to accommodate an intro image and a description image for the first couple of seconds, this is the code I used when I recorded the Good Bot music video.

Mr. Good Bot listening to music:

 

Fireflies

I mentioned that it’s possible to use this methodology with non-face related images to varying degrees of success.

Here’s a screenshot of a Mr. Good Bot rendering a scene of fireflies in a jar.

Mr.Good Bot Fireflies

I think the images are pretty but the effect of the fireflies being in one place then instantaneously in another doesn’t look right. It’s too sudden and doesn’t reflect how real fireflies buzz around and flicker.

Also some of the patterns just look weird but I think switching to frames containing groups of fireflies instead of rendering each firefly position as a separate image would improve the patterns.

Additionally and or alternatively, perhaps a slot system (an array with a limited specified size) that holds lit firefly positions (like in a cookie) and randomly replaces a few of the fireflies each turn so some would remain stable and lit longer while others seem to move around and flicker more.

Ultimately, to improve the fireflies example a method of generating sequences would need to be devised and likely we would want higher FPS (see Composite Animation).

That would mean using a different animation strategy and since this is a prototype I find myself compelled to KISS Mr. Good Bot! 😛

This method looks better with Mr. Good Bot because even if we anthropomorphize him we expect him to have mechanical limitations that cause him to not perfectly mimic the way a human moves, and the illusion is sufficient, whereas the fireflies are presented against a nice background so we expect more from the animation than the fireflies can deliver by teleporting around the jar.

GitHub

All of the code and images for Mr. Good Bot are available to download on GitHub for free.

GitHub: MrGoodBot

 

I think given that Halloween is next week we’ll take a break from Mr. Good Bot and do something festive, though it will still be bot related so I will see you then!


CODE JUMP SCARE!

<?php

/*

If you like my work, consider supporting my work on Patreon. Thank you! 🙂

But, if all you can do is like, share, comment and subscribe… well that’s cool too!

*/

if($You->Like($My->Work) === TRUE){
    if($You->Consider($You->SupportOnPatreon($My->Work)) === TRUE){
        echo 'Thank you! :-)';
    }
}
else{
    $cool_limits = array("like", "share", "comment", "subscribe");
    
    foreach($You->Limits as $all_you_can_do){
        if(in_array($all_you_can_do, $cool_limits) === TRUE){
            echo 'Well that’s cool too!';
        }
    }
    
}

?>

Much Love,

~Joy