Search

Geek Girl Joy

Ancestor Simulations Generating Galaxies

Welcome back to another installment of my Ancestor Simulations series, previous posts in this series series are:

Carl Sagan famously wrote these two excerpts in his book Cosmos:

“If you wish to make an apple pie from scratch, you must first invent the universe.” & “The nitrogen in our DNA, the calcium in our teeth, the iron in our blood, the carbon in our apple pies were made in the interiors of collapsing stars. We are made of star stuff.”

Fundamentally what Mr. Sagan is talking about is that we are “here and now” in ‘spacetime‘ and that everything you see around you, from the seemingly mundane soil beneath your feet to the silicon chips in the device you’re using to read this post right down to even the very constituents of your own body all originate not here on earth, but out there among the stars!

Our Sun (Sol) is ~4.6 billion years old and when our solar system formed, it did so by accreting and coalescing material already present in our galaxy. It turns out that microgravity is an incredibly potent force for for creating emergent larger structures (like galaxies and solar systems) from small clumps of available materials. Those materials came from somewhere outside our solar system (we are the remnants of other long dead solar systems) to form or become our solar system (the Sol system).

Below I have linked to a a video of Astronaut Don Pettit PhD and Science Officer for Expedition 6 of the ISS explaining an experiment he conducted that illustrates how microgravity would have operated in our early solar system to take “small particles that are bouncing around colliding with each other and coagulating into larger agglomerates”.

Those “small particles” Mr. Pettit is talking about in his experiment were salt (Na) and is analogous to the ‘Interstellar Medium‘  which is ultimately the byproduct of the life cycles of stars that existed and “died” before our system had even formed.

In time (~5 Billion years from now) our sun is expected to expand into a ‘Red Giant‘ at which point it is believed that it will consume our little Earth and anything still around on the planet. If Sol remains intact and isn’t itself consumed by a black hole or by some other celestial body, it will progress through it’s life cycle to become a ‘White Dwarf‘. Trillions of years later Sol may eventually become a ‘Black Dwarf‘ star after scattering it’s heavy elements into interstellar space as a ‘Planetary Nebula‘ thereby sowing the seeds (that ‘star stuff’ Mr. Sagan wrote about) of countless other worlds, some of which may eventually harbor life just like our solar system does now. Some of that ‘star stuff’, will be the very same particles that are part of your body at this very moment!

The Milky Way‘ (our home galaxy) is a vast area spanning ~100,000 light years in diameter and and ~2000 light years thick. It is estimated that the Milky Way Galaxy is ~13.7 Billion years old, making it almost as old as the universe itself!

Which now brings us to this week’s project, Generating Galaxies. The method we will use to create galaxies today is called a Logarithmic Spiral and it will allow us to create all the main distinct types of galaxy: Spiral, Quasar, Globular & Irregular.

So without further hemming and hawing, here is our code:



set_time_limit(300); // 5 Minutes adjust as needed
ini_set('memory_limit', '3G'); // 3 GB Adjust as needed

function RandomFloat(){
    return '0.'. mt_rand(0,9999);
}

function MinMax($value, $min, $max, $size){
    return abs(($value - $min) / ($max - $min) * $size);
}

function vignette($im){
    $width = imagesx($im);
    $height = imagesy($im);

    for($row = 0; $row < $width; ++$row){
        for($col = 0; $col < imagesy($im); ++$col){  
            $index = imagecolorat($im, $row, $col);
            $rgb = imagecolorsforindex($im, $index);
            $sharp = 1; // 0 - 10 small is sharpnes, 
            $level = 0.7; // 0 - 1 small is brighter    
            $l = sin((pi() + RandomFloat()) / $width * $row) * sin((pi() +  RandomFloat()) / $height * $col);
            $l = pow($l, $sharp); 
            $l = 1 - $level * (1.2 - $l);
            $rgb['red'] *= $l;
            $rgb['green'] *= $l;
            $rgb['blue'] *= $l;
            $color = imagecolorallocate($im, $rgb['red'], $rgb['green'], $rgb['blue']);
            imagesetpixel($im, $row, $col, $color);  
        }
    }
    return(true);
}

if(isset($_POST["galaxy-type"])){$galaxy_type = $_POST["galaxy-type"];}
else{$galaxy_type = 'spiral';}


if(isset($_POST["exponent"])){$exponent = $_POST["exponent"];}
else{$exponent = 9;}

$size = pow(2, $exponent);

if(isset($_POST["apply-gaussian"])){$apply_gaussian = $_POST["apply-gaussian"];}
else{$apply_gaussian = false;}

if(isset($_POST["apply-colorize"])){$apply_colorize = $_POST["apply-colorize"];}
else{$apply_colorize = false;}

if(isset($_POST["red"])){$red_number = $_POST["red"];}
else{$red_number = mt_rand(0,255);}

if(isset($_POST["green"])){$green_number = $_POST["green"];}
else{$green_number = mt_rand(0,255);}

if(isset($_POST["blue"])){$blue_number = $_POST["blue"];}
else{$blue_number = mt_rand(0,255);}

if(isset($_POST["apply-greyscale"])){$apply_greyscale = $_POST["apply-greyscale"];}
else{$apply_greyscale = false;}

if(isset($_POST["apply-negate"])){$apply_negate = $_POST["apply-negate"];}
else{$apply_negate = false;}

if(isset($_POST["apply-vignette"])){$apply_vignette = $_POST["apply-vignette"];}
else{$apply_vignette = false;}


if(isset($_POST["number-of-stars"])){$number_of_stars = $_POST["number-of-stars"];}
else{$number_of_stars = 300000;}

$a = 0.9759 + RandomFloat();
$b = 0.1759; // ~nautilus shell
$steps = 5; 
$radius = 3 * pi()* $steps;
$max_spread = 0.3;
$min_star_position = -76458.735624534;
$max_star_position = 102693.535433 ;

// Image Resource
$galaxy = imagecreatetruecolor($size, $size);

// Colors
$white = imagecolorallocate($galaxy, 255, 255, 255);
$yellow = imagecolorallocate($galaxy, 255,255,0);
$red = imagecolorallocate($galaxy, 255,0,0);
$blue = imagecolorallocate($galaxy, 0,0,255);
$black = imagecolorallocate($galaxy, 0, 0, 0);
$dark_grey = imagecolorallocate($galaxy, 50, 50, 50);

// Create Image of Galaxy
for($i = 0; $i  $number_of_stars * 0.6){
          $radius = 6 * pi()* $steps; // increase radius and spread out
      }
   }
   elseif($galaxy_type == 'quasar'){// Quasar Galaxy
      if($a != 1){
          $a = mt_rand(1, 2) + RandomFloat();
          $b = 0.2;
          $max_spread = RandomFloat();
      }
      
      $row = $a * exp($b * $angle) * cosh($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sinh($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
  elseif($galaxy_type == 'globular'){// Globular/Elliptical Galaxy
      if($max_spread != 1){     
          $a = mt_rand(1, 2) + RandomFloat();
          $b = 0.2;      
          $max_spread = 1;
          $radius = 6 * pi()* $steps;
      }
      
      $row = $a * exp($b * $angle) * cos($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sin($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
  elseif($galaxy_type == 'irregular'){// Irregular Galaxy
      if($max_spread != 1){     
          $a = 8 + RandomFloat();
          $b = 0.2;      
          $max_spread = 1.3;
          $radius = 6 * pi()* $steps;
      }
      
      $row = $a * exp($b * $angle) * cos($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sin($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
    
  // Flip a coin to determine which arm 
  // the star should be on.
  if (mt_rand(0, 1) == 1){
      // if heads put it on the second arm
      // by inverting the values
      $row = ($row/-1);
      $col = ($col/-1);
  } 

  // Normalize positions to be within the image bounds
  $row = MinMax($row, $min_star_position, $max_star_position, $size);
  $col = MinMax($col, $min_star_position, $max_star_position, $size);

  
  $colors = array($white, $white, $white, $dark_grey, $yellow, $red, $blue);
  @imagesetpixel($galaxy,round($row),round($col), $colors[mt_rand(0,6)]); // Plot the star position
}

if($apply_gaussian == true){
    imagefilter($galaxy, IMG_FILTER_GAUSSIAN_BLUR); // blur
}
if($apply_colorize == true){
    @imagefilter($galaxy, IMG_FILTER_COLORIZE, $red_number, $green_number, $blue_number); // Shift colors
}
if($apply_greyscale == true){
    @imagefilter($galaxy,  IMG_FILTER_GRAYSCALE); // grey scale
}
if($apply_negate == true){
    @imagefilter($galaxy, IMG_FILTER_NEGATE); // inverse colors
}
if($apply_vignette == true){
    @vignette($galaxy); // vignette
}


// Output image
imagepng($galaxy, "galaxy.png");

// free memory
imagedestroy($galaxy);

You may view a live version of this project here if you would like and access the source for this project on GitHub here.

You can see the results of this code in the images below.

Click for Full Size Images

Spiral

Globular

Quasar

Irregular

That’s it for today ladies and gentlemen.

Please Like, Comment below & Share this post with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Ancestor Simulations Generating Planet Terrain Friday Fun Fix

Surprise Friday post everybody! 😋

First, relax. Yes you will get another post on Monday, I’m working on the next part of our Ancestor Simulation and I think I have something you will really enjoy but in the mean time I wanted to do a second post on Generating Planet Terrain.

Why a second post? Well, to be honest… I know the week long wait for more projects is tough, so I’m trying to produce more content for you guys! I tend to prefer to focus on creating quality projects and code and frankly that takes time, but I know some of you really need your fix. So, I’m experimenting with the idea of “documenting the process” as a way of letting you guys “peer behind the curtain” so to speak.

I can’t guarantee I will  always do a second post or that I will only do two posts. I’m merely committing to produce more content on the whole and this post is part of that.

Anyway, I hope you have been enjoying the Ancestor Simulation series, I have received an overwhelming positive response from my readers about this topic and that might strike you as quite odd when you consider the fact that my content is quite “dry” and frequently full of long complex code but I think that speaks to the idea that if you make your content interesting enough your ‘target audience’  (the smart techies who read my posts) will keep coming back, and for that… I am very grateful! 🙂

So on to Ancestor Simulations. No code today, or rather if you want to get the updated code you can get a copy of this project over on GitHub for free. While you are there be sure to follow me and star the repo so I know you like where the project is going. Speaking of which… just how far am I willing to go with this project? Well that depends on you guys, keep letting me know you are interested and feel free to suggest anything you want to see our sim do and I will see if I can’t accommodate 😉 however ultimately I am implementing this for myself because I find it interesting, I hope you do as well.

This past Monday I posted the Generating Planet Terrain post and before we move forward I wanted to add a few additional features and changes:

  1. More color variation possibilities via an additional ‘Color Variance’ pass over the terrain data and more manual access to the coloring system. It’s still based on a ‘Terrain Color Palette’ however this second pass allows MANY more color combinations which ultimately makes each planet feel more unique and special.
  2. Added a Generate Random button.
  3. Slight change to the water system.
  4. Slight change to the ice system.
  5. Added a ‘Display Type’ setting so you can now view the map as a ‘Map’ (as before) or as a ‘Planet’ (see below).

You can of course preview a running version here.

And with that I hope you all have a great weekend and I will see you Monday.

Click for Full Size Images

Please Like,  Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Ancestor Simulations Generating Planet Terrain

Welcome back, I hope you have been missing me all week! 😛

It’s Monday which means that it’s time to continue with the second installment of our Ancestor Simulations series.

Last week I introduced the topic of Ancestor Simulations and illustrated how we can implement a version of Drake’s Equation ( N = R* * fp * Ne * fl * fi * fc * L ) so that we can probabilistically run many different simulations on a hypothetical galaxy (or even universe) and depending on the values we input for the axioms of equation we get vastly different types of galactic neighborhoods. In some simulations the universe is a vast empty space that is cold & lifeless, yet in others it is teeming with life practically a stones throw outside of our local star group in any direction you care to look. I won’t venture to speculate if they really are out there or not but I will give you a quote by Carl Sagan that I like:

“The universe is a pretty big place. If it’s just us, seems like an awful waste of space.”

Now that we have an equation for defining and describing the universe that our simulation takes place in we will want to find number ranges we feel comfortable with for our simulation and cache them at some point so we can make use of them in other parts of the simulation but for now I would like to move away from Drake’s Equation and move to a more practical challenge.

In order for our “ancestors” to be comfortable and build happy productive lives for themselves they will need a place to live. We have Earth to call our home sweet chunk of terraferma 😛

However I think a kind of a fun aspect to this project is that the so called “ancestors” need not be our ancestors and, hell they need not even be primates! They could end up being intelligent arthropods if we were to build some kind of genetic evolutionary system!

So because it’s far more interesting (not to mention more useful) to generate a terrain from scratch than to simply use a pre-rendered picture or mesh of earth (especially if we are not simulating earth)  it becomes evident that we need an algorithm to build a planetary terrain before we can even consider concepts like evolution creating critters.

The algorithm I selected is sometimes called the ‘Plasma fractal’ (which goes hand in hand with the post I did not that long ago about Sierpinski Triangle’s called A Chaos Game ) though most people probably better know this algorithm as the ‘Diamond Square Algorithm‘.

The Plasma Fractal Algorithm

The plasma ‘diamond-square’ algorithm begins with a 2D square array of width and height 2n +1.

That looks like this in code

$size = pow(2, $exponent);

Basically this says take the number 2 and multiply it by n (the Exponent). In my implementation I start the grid count in the top left corner as row: 0  col: 0 (as opposed to (1,1)) therefore I do not have to add 1 to the $size variable.

We then create our 2D array:

$terrain = array_fill(0, $size+1, array_fill(0, $size+1, NULL));

Basically this says create and array of arrays $size+1 wide and $size+1 high and fill the spaces with NULL characters.

Now we start creating the terrain

The four corner points of the array are assigned initial random values within a range (lets say -4 through 4).


$a = $terrain[0][0] = mt_rand($min_roughness, $max_roughness);
$b = $terrain[0][$size] = mt_rand($min_roughness, $max_roughness);
$c = $terrain[$size][0] = mt_rand($min_roughness, $max_roughness);
$d = $terrain[$size][$size] = mt_rand($min_roughness, $max_roughness);

 

Find the center and compute the average

Next you compute the average of the 4 outer squares, find the center square and set it’s value to be the average of the 4 outer square positions + random value within a range.


$average = $a + $b + $c + $d;

$row = $size/2;
$col = $size/2;
$e = $terrain[$row][$col] = ( $average + mt_rand($min_roughness, $max_roughness)) / 5;

 

Divide, Rinse, Repeat

Now all that is left to do is to iterate through the array and perform the  square & diamond steps until all positions in the array have been set. Each time you iterate through the array you divide the array into smaller and smaller “chunks” of equal size and step through each chunk of the array doing the diamond and square walks.

That looks like this:


$chunk_size = $size;
for($level = 1; ($chunk_size / $level) > 0.1; $level++){
	for($row_offset = 0; $row_offset <= $size; $row_offset+=$chunk_size){
		for($col_offset = 0; $col_offset <= $size; $col_offset+=$chunk_size){
			
			// Do (Diamond Step)
			// if the position is not already set then set it
			if(!isset($terrain[$row_offset - $chunk_size/2][$col_offset])){
				$terrain[$row_offset - $chunk_size/2][$col_offset] = ($average + mt_rand($min_roughness, $max_roughness)) / 5;
			}
			$a = $terrain[$row_offset - $chunk_size/2][$col_offset];
			
                        // if the position is not already set then set it
			if(!isset($terrain[$row_offset][$col_offset - $chunk_size/2])){
				$terrain[$row_offset][$col_offset - $chunk_size/2] = ($average + mt_rand($min_roughness, $max_roughness)) / 5;
			}
			$b = $terrain[$row_offset][$col_offset - $chunk_size/2];
			
			// if the position is not already set then set it
			if(!isset($terrain[$row_offset][$col_offset + $chunk_size/2])){
				$terrain[$row_offset][$col_offset + $chunk_size/2] = ($average + mt_rand($min_roughness, $max_roughness)) / 5;
			}
			$c = $terrain[$row_offset][$col_offset + $chunk_size/2];
			
			// if the position is not already set then set it
			if(!isset($terrain[$row_offset + $chunk_size/2][$col_offset])){
				$terrain[$row_offset + $chunk_size/2][$col_offset] = ($average + mt_rand($min_roughness, $max_roughness)) / 5;
			}
			$d = $terrain[$row_offset + $chunk_size/2][$col_offset];
			
			// Compute the average height of $a + $b + $c + $d
			$average = $a + $b + $c + $d;
						
			// Set Center (Square Step)
			// if the position is not already set then set it
			if(!isset($terrain[$row_offset + $chunk_size/2][$col_offset + $chunk_size/2])){
				$terrain[$row_offset + $chunk_size/2][$col_offset + $chunk_size/2] = ($average + mt_rand($min_roughness, $max_roughness)) / 5;
			}
			$e = $terrain[$row_offset + $chunk_size/2][$col_offset + $chunk_size/2];
		}
	}

	// Reduce the chunk size
	$chunk_size = $chunk_size/2;
}

 

After which you simply create an image that is the same size of the array and use the array values as the colors. Lower values should be darker and higher values should be lighter. Water is added by declaring a constant water level and anything below a given level is water and colored the same but as shades of blue.

Putting it all together

And with that all you have to do is create an HTML interface!

Preview a running version of this project here and get your copy of the entire project over on GitHub here.

Click for Full Size Images

Small Lush Islands

 

Desert

 

Lush Landmass with Large Lakes

 

Lunar Like Surface

Obviously more can be added to the generator however this is a great jumping off point to get you started! If you have any thoughts or ideas on improvements or you simply like these kinds of posts go ahead and leave a comment below.

Please Like,  Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Ancestor Simulations and Drakes Equation

Today we are going to begin exploring the concept of Ancestor Simulations. Why “ancestor simulations”?  Well as a you know, I am GeekGirlJoy (I should really trademark that huh? 😛 Let’s just say I call dibs! 😉 ) so I enjoy SCIENCE!!! therefore what better way to get paid than to employ my 1337 compsci skills by teaching you a little something about simulations and amuse myself with my own virtual denizens in the process… come closer padawan there is much to discuss…

In a nutshell the concept purposed by Nick Bostrom in 2003 goes like this:

  1. “The fraction of human-level civilizations that reach a posthuman stage (that is, one capable of running high-fidelity ancestor simulations) is very close to zero”, or
  2. “The fraction of posthuman civilizations that are interested in running ancestor-simulations is very close to zero”, or
  3. “The fraction of all people with our kind of experiences that are living in a simulation is very close to one”

Personally I tend to agree with #1 outright simply because it “jibe’s right” although it is important to stress that “very close to zero” part. Very close to zero is NOT zero and even if the odds of there being other intelligent “tool making” species who have advanced to the point of being so called “posthuman” are really low, it’s not zero.

Further, if you spend any time at all considering the Drake Equation you are forced to consider the possibility if not outright conclude that even if they are so far away that we can never see or communicate with them they must be there and, likely not just a handful either!

Below is some code so you can experiment with the Drake Equation yourself! Its setup so that you can call the DrakeEquation() function without arguments and it will randomly select values within the appropriate margins.

The Drake Equation: N = (R* * fp * Ne * fl * fi * fc * L)

 


<?php

// Reference the Drake Equation: https://en.wikipedia.org/wiki/Drake_equation

function DrakeEquation($Rs = 0, $Fp = 0, $Ne = 0, $Fl = 0, $Fi = 0, $Fc = 0, $L = 0){

	// The average rate of star formation in our galaxy.
	// Per the aforementioned Wiki Article:
	/*
	Latest calculations from NASA and the European Space Agency indicate that the current rate of star formation in our galaxy is about 0.68–1.45 M☉ of material per year.[26][27] To get the number of stars per year, this must account for the initial mass function (IMF) for stars, where the average new star mass is about 0.5 M☉.[28] This gives a star formation rate of about 1.5–3 stars per year.
	*/
	if(empty($Rs)){
	    $Rs = mt_rand(0,3) . '.' . mt_rand(0,99);
	}

	// Fraction of stars with so called "Habitable planets".
	// Per the aforementioned Wiki Article:
	/*
	Recent analysis of microlensing surveys has found that fp may approach 1—that is, stars are orbited by planets as a rule, rather than the exception; and that there are one or more bound planets per Milky Way star.[29][30]
	*/
	if(empty($Fp)){
		$Fp_decimal = mt_rand(0,99); 
		if($Fp_decimal == 0)
		{
			$Fp_decimal = mt_rand(0,9); // this may not be absolute 0
			                            // so if it is we will set it to 1/100th - 9/100th
		}
	    $Fp = "0.$Fp_decimal";
    }
	
	
	

	// Average number of planets that can potentially support life per star that has planets
	// Per the aforementioned Wiki Article:
	/*
	In November 2013, astronomers reported, based on Kepler space mission data, that there could be as many as 40 billion Earth-sized planets orbiting in the habitable zones of sun-like stars and red dwarf stars within the Milky Way Galaxy.[31][32] 11 billion of these estimated planets may be orbiting sun-like stars.[33] Since there are about 100 billion stars in the galaxy, this implies fp · ne is roughly 0.4. The nearest planet in the habitable zone may be as little as 12 light-years away, according to the scientists.[31][32]
	*/
	if(empty($Ne)){
		$Ne_decimal = mt_rand(0,99); 
		if($Ne_decimal == 0)
		{
			$Ne_decimal = mt_rand(0,9); // this may not be absolute 0 since were here
			                            // so if it is we will set it to 1/100th - 9/100th
		}
	    $Ne = mt_rand(0,1) . '.' . $Ne_decimal;
    }

	// Fraction of habitable planets that actually develop life.
	/* 
	We're here (humans) so it can't be 0.00 and we don't know that it's 1.00 because then it would mean that wherever life evolves, intelligent live WILL ALWAYS evolve EVENTUALLY... and we don't know that is the case, so we'll set this as such:
	*/
	if(empty($Fl)){
		$Fl_decimal = mt_rand(0,99); 
		if($Fl_decimal == 0)
		{
			$Fl_decimal = mt_rand(0,9); // this may not be absolute 0
			                            // so if it is we will set it to 1/100th - 9/100th
		}
		$Fl = "0.$Fl_decimal";
    }

	// Fraction of habitable planets with life on them that also evolve intelligent life
	/*
	Defined using the "short scale" https://en.wikipedia.org/wiki/Long_and_short_scales
	0.001 one of one thousand planets with life, will evolve intelligent life.
	0.0001 one out of ten thousand planets with life, will evolve intelligent life.
	0.00001 one out of one hundred thousand planets with life, will evolve intelligent life.
	0.000001 one out of one million planets with life, will evolve intelligent life.
	0.0000001 one out of ten million planets with life, will evolve intelligent life.
	0.00000001 one out of one hundred million planets with life, will evolve intelligent life.
	0.000000001 one out of one billion planets with life, will evolve intelligent life.
	0.0000000001 one out of ten billion planets with life, will evolve intelligent life.
	0.00000000001 one out of one hundred billion planets with life, will evolve intelligent life.
	0.000000000001 one  out of one trillion planets with life, will evolve intelligent life.
	*/
	$Fi = '0.00' . str_repeat('0', mt_rand(0,9)) . '1';

	// Fraction that survive long enough to sufficiently develop long range communications technology (wireless transmissions)
	if(empty($Fc)){
		$Fc_decimal = mt_rand(0,99); 
		if($Fc_decimal == 0)
		{
			$Fc_decimal = mt_rand(0,9); // this may not be absolute 0
			                            // so if it is we will set it to 1/100th - 9/100th
		}
		$Fc = "0.$Fc_decimal";
    }

	
	// How long those civilizations last
	$L = mt_rand(500,25000); // Anyone's guess could be indefinite
	                         // So lets have it max at 25K years

	return $Rs * $Fp * $Ne * $Fl * $Fi * $Fc * $L;

}

// the number of planets with detectable signs of life
echo DrakeEquation();

?>

 

If you would like to get a copy of this code you can get it over on GitHub here.

It’s important to note two things however:

  1. The value returned by the Drake Equation represent how many other advanced civilizations (aliens) there are and the values returned by the Drake Equation will fluctuate depending on the values used.
  2. Because not all the values are known and we have to guess at what they are, we tend to think of the Drake Equation not as an algorithm that yields truth (like e=MC2) but rather as a tool that helps us test the possibilities.

In regards to Bostrum’s #2, no arguments here. It is basically just saying that even if possible to do so, many civilizations will simply opt not to do so, whatever their reasons may be, but again even if just a few or even one (us/humans) chose to do so then #2 becomes “irrelevant” because not ALL civilizations will chose to do ancestor simulations but some (we/humans) will.

This brings us to #3, basically Bostrom is saying that if #1 & #2 are not absolutely false, #3 has to be correct simply due to probability. I definitely concede points 1 & 2. However #3 is saying that YOU & I must conclude that the odds are VERY high that you and I are in one of those so called “ancestor simulations”, so therefore we must be.

This one bothers me a bit… not because I have any real problem with the idea we are all living in a simulation, we could be, however it simply seems like a “lazy” conclusion that isn’t substantiated by any empirical evidence.

Yes, its true I asked you to consider the Drake Equation but I would be remiss in leading you to believe that you should accept any of the numbers produced by it as hard facts and not thought experiments. Although one day we may know all the right values to use in the Drake Equation and then of course it would rest on a solid empirical footing.

Having a high probability that we are in a simulated reality does not automatically make it so and I will give you some examples.

Consider the US “Powerball” Lottery.  the odds are 175,223,510 to 1 against you, for a probability of 0.0000005707% of wining. Put a different way, 1 out of 175 Million tickets should win the “Grand Prize”… but even with such long odds… people play and still win. Most lose, the game requires it in fact and you would basically be correct in believing that if you bought a handful of tickets every day of your life you would still lose. However, people say hang the odds and promptly lose their money but 0.0000005707% of them… win.

Now consider image recognition Neural Networks, they basically operate off of probability and even though they can get amazingly accurate with their responses, they DO make errors (see wiki articles on Confabulation for example) because of their probabilistic approach to problem solving. There are examples where white noise images are given to Convolution Neural Networks and they will say it’s a dog or something even though it’s clearly evident to you and I that it is not a dog.

I could also point out that your odds of dying in a commercial plane are something like one one-hundred-thousandth of one percent (.000014% – basically impossible) and yet here is a list of all the commercial airplane crashes that based on probability should not have happened!

My point is to show that operating on probability alone is not enough! It’s not enough to say that we are “probably” in a simulation therefore we are because even if the odds are exceedingly against us being in “base reality” (true non-simulated reality) it fails to prove or disprove anything and as proofs of my position I have provided the examples that some people win the lotto, neural networks occasionally make mistakes and airplanes do occasionally fall out of the sky!

Now that we are past the hurdle of if we are or not in a simulation, the answer is it doesn’t matter and to quote Rick Sanchez (Rick and Morty) “it’s best not to think about it” :-P, we can talk about building an Ancestor Simulation of our very own!

It goes without saying that our simulated creatures will not be “conscious” or “self-aware” like you and I and therefore falls short of a true Ancestor Simulation like Bostrum is talking about but it very well could be our code base that leads to the creation of the first real ancestor sim.

In any event I will be developing this concept over the next week and I hope have something really interesting to bring you next Monday!

Please Like, Comment & Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Tickle the Monday Ivories

Its Monday so hopefully I can bring even just a little Joy into your life today! 😛

Mondays are sort of my Friday in the sense that I’ve tied up all lose ends from the previous week, updated my clients on their projects or sites, fed Xavier countless times (and changed almost as many diapers) and generally my 80ish hour work week is coming to a close, though I don’t have long to ‘rest on my laurels‘, its back to the grind this evening but right now I owe you all a project don’t I? 😉

As you may know I am funding this little experiment with the proceeds I get on Patreon and as such I am limited sometimes in what I can do for you guys in some ways. This does provide me with a nice challenge every week to bring you something interesting that also costs me nothing and generally this has not been an issue since most of the projects we’ve done together have been “back-end” code focusing on PHP and C# with the exception of the Magic 8 Ball post I published in February.

This post will again showcase a “front-end” project that could run right here on this page save for the fact that I am currently using a free WordPress.com account instead of a paid account with more features or more preferentially something like a VPS which will let us do basically whatever we wanted. Now of course there are other options and when it’s time I will weigh the options and ensure that we get the most bang for our buck (meaning the highest marginal utility for the lowest marginal cost) but until then, most unfortunately WordPress.com wont let me embed any functional code on these pages and that includes JavaScript.

Additionally, there are times when you will see ads on my content. I get nothing from them and I cannot remove them. So, if you like these posts and would like MORE out of each then head over to Patreon and drop a buck or more in my hat every month and you will be making a meaningful contribution to growing the quality of my content and at only $12 a year that’s a huge bargain!

So with that, lets to a little Q&A on today’s project.

Q: Why a Piano/Keyboard?

A: Cause its cool! Next question. 😛

Q: Why is there is no PHP in this post?

A: There is actually a tiny amount of PHP in this project which I use to switch between the SVG and the PNG but outside of that there is no PHP tied into the core of this project. I honestly thought about it but  I wanted to keep it simple. Building a music playing neural network on top of the piano did cross my mind but again, this post is about showing that we can build something entirely awesome without it also being overly complex so… I nixed it. Maybe some other time. 😉

Q: Why do the same project using SVG and PNG?

A: The first and most simplest reason is to demonstrate the principal of there being more than one way to do something, but also because there are differences. The SVG uses inline hyper links wrapped around the <g> (groups) in the SVG whereas the PNG uses an image map with the Javascript instead attaching to the link in the map and not the image itself.

Of course the SVG will scale flawlessly on any size screen and the PNG is shorter to code out but will get pixelated the more you zoom in or resize it to be larger.

 

Playable Piano / Keyboard

This could be playable! Support me on Patreon so projects like this can be previewed.

keyboard

Live Preview

You can preview a live version of this project over here

The Code


<!DOCTYPE html>
<html>
    <head>
        <title>Playable SVG & PNG Keyboard</title>
        <style>
        body {
            margin: 0;
            padding: 0;
            text-align: center; /* !!! */
            background-color: #222222;
        }
        h1,p {
            color:white;
        }

        .centered {
            margin: 0 auto;
            text-align: left;
            width: 600px;
        }
        </style>
    </head>
    <body>
        <audio id="C-Audio"><source src="audio/C.wav" type="audio/wav">
          Your browser doesn't support the audio element.
        </audio>
        <audio id="D-Audio"><source src="audio/D.wav" type="audio/wav"></audio>
        <audio id="E-Audio"><source src="audio/E.wav" type="audio/wav"></audio>
        <audio id="F-Audio"><source src="audio/F.wav" type="audio/wav"></audio>
        <audio id="G-Audio"><source src="audio/G.wav" type="audio/wav"></audio>
        <audio id="A-Audio"><source src="audio/A.wav" type="audio/wav"></audio>
        <audio id="B-Audio"><source src="audio/B.wav" type="audio/wav"></audio>
        <audio id="C-Sharp-Audio"><source src="audio/C-Sharp.wav" type="audio/wav"></audio>
        <audio id="D-Sharp-Audio"><source src="audio/D-Sharp.wav" type="audio/wav"></audio>
        <audio id="F-Sharp-Audio"><source src="audio/F-Sharp.wav" type="audio/wav"></audio>
        <audio id="G-Sharp-Audio"><source src="audio/G-Sharp.wav" type="audio/wav"></audio>
        <audio id="A-Sharp-Audio"><source src="audio/A-Sharp.wav" type="audio/wav"></audio>


        <?php
        if(isset($_POST['method'])){
            $method = $_POST['method'];
        }
        else{
        $method = "svg";
        }
        ?>

        <div class="centered">
        <?php if($method == "svg"){ ?>
            <h1>SVG</h1>
        <?php } elseif($method == "png"){ ?>
            <h1>PNG</h1>
        <?php } ?>
            <p>Click the keys to play a little tune!</p>

            <form action="#" method="POST">
                <select id="method" name="method" onchange="this.form.submit()">
                  <option value="svg" <?php if($method == "svg"){ echo "selected"; }?>>SVG - Inline Hyperlinks</option>
                  <option value="png" <?php if($method == "png"){ echo "selected"; }?>>PNG - ImageMap Hyperlinks</option>
                </select> 
            </form>
            
        <?php if($method == "svg"){ ?>
            <svg
           xmlns:dc="http://purl.org/dc/elements/1.1/"
           xmlns:cc="http://creativecommons.org/ns#"
           xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
           xmlns:svg="http://www.w3.org/2000/svg"
           xmlns="http://www.w3.org/2000/svg"
           xmlns:xlink="http://www.w3.org/1999/xlink"
           xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
           xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
           width="600"
           height="500"
           viewBox="0 0 158.75 132.29167"
           version="1.1"
           id="geekgirljoys-playable-keyboard"
           inkscape:version="0.92.0 r15299"
           sodipodi:docname="keyboard.svg"
           inkscape:export-filename="C:\Users\Joy\Desktop\Piano\New\piano.png"
           inkscape:export-xdpi="300"
           inkscape:export-ydpi="300"
           style="enable-background:new">
          <defs
             id="defs12918">
            <linearGradient
               inkscape:collect="always"
               id="linearGradient5259">
              <stop
                 style="stop-color:#003380;stop-opacity:1;"
                 offset="0"
                 id="stop5255" />
              <stop
                 style="stop-color:#003380;stop-opacity:0;"
                 offset="1"
                 id="stop5257" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               id="linearGradient5021">
              <stop
                 style="stop-color:#ffffff;stop-opacity:1;"
                 offset="0"
                 id="stop5017" />
              <stop
                 style="stop-color:#ffffff;stop-opacity:0;"
                 offset="1"
                 id="stop5019" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               id="linearGradient4664">
              <stop
                 style="stop-color:#ffffff;stop-opacity:1"
                 offset="0"
                 id="stop4660" />
              <stop
                 style="stop-color:#000000;stop-opacity:0;"
                 offset="1"
                 id="stop4662" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               id="linearGradient4614">
              <stop
                 style="stop-color:#000000;stop-opacity:1;"
                 offset="0"
                 id="stop4610" />
              <stop
                 style="stop-color:#000000;stop-opacity:0;"
                 offset="1"
                 id="stop4612" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               id="linearGradient15315-5">
              <stop
                 style="stop-color:#f9f9f9;stop-opacity:1;"
                 offset="0"
                 id="stop15311" />
              <stop
                 style="stop-color:#000000;stop-opacity:0.79605263"
                 offset="1"
                 id="stop15313" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               id="linearGradient15157">
              <stop
                 style="stop-color:#333333;stop-opacity:1"
                 offset="0"
                 id="stop15153" />
              <stop
                 id="stop15161"
                 offset="0.5"
                 style="stop-color:#191919;stop-opacity:1;" />
              <stop
                 style="stop-color:#333333;stop-opacity:1"
                 offset="1"
                 id="stop15155" />
            </linearGradient>
            <linearGradient
               inkscape:collect="always"
               xlink:href="#linearGradient15157"
               id="linearGradient15159"
               x1="79.876411"
               y1="325.5043"
               x2="79.886864"
               y2="264.30887"
               gradientUnits="userSpaceOnUse"
               gradientTransform="translate(1.3680627e-7,-79.375004)" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient15315-5"
               id="radialGradient15736"
               cx="38.679722"
               cy="236.95551"
               fx="38.679722"
               fy="236.95551"
               r="12.541217"
               gradientTransform="matrix(5.7072155,0.26868388,-0.01027659,0.21828878,-179.63869,174.83816)"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient4614"
               id="radialGradient4616"
               cx="85.9496"
               cy="328.67267"
               fx="85.9496"
               fy="328.67267"
               r="79.375"
               gradientTransform="matrix(1.7919596,0.01649915,-8.0047577e-4,0.08693941,-62.780617,263.66084)"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient15315-5"
               id="radialGradient4634"
               cx="77.359444"
               cy="176.91103"
               fx="77.359444"
               fy="176.91103"
               r="25.082434"
               gradientTransform="matrix(1.3135871,-0.1191492,0.02348472,0.25891276,-28.413636,140.32382)"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient4664"
               id="radialGradient4666"
               cx="13.091763"
               cy="235.10512"
               fx="13.091763"
               fy="235.10512"
               r="16.501682"
               gradientTransform="matrix(2.2777563,-0.10674805,0.01546079,0.32989785,-20.362998,158.94195)"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5021"
               id="radialGradient5086"
               cx="77.261093"
               cy="169.72464"
               fx="77.261093"
               fy="169.72464"
               r="1.3028055"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5021"
               id="radialGradient5146"
               cx="77.261093"
               cy="169.72464"
               fx="77.261093"
               fy="169.72464"
               r="1.3028055"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5021"
               id="radialGradient5174"
               cx="77.261093"
               cy="169.72464"
               fx="77.261093"
               fy="169.72464"
               r="1.3027751"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5021"
               id="radialGradient5199"
               cx="77.261093"
               cy="169.72464"
               fx="77.261093"
               fy="169.72464"
               r="1.3028055"
               gradientUnits="userSpaceOnUse" />
            <radialGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5021"
               id="radialGradient5239"
               cx="101.47026"
               cy="169.59991"
               fx="101.47026"
               fy="169.59991"
               r="1.3028055"
               gradientUnits="userSpaceOnUse" />
            <linearGradient
               inkscape:collect="always"
               xlink:href="#linearGradient5259"
               id="linearGradient5265"
               x1="-3.1599205"
               y1="235.1051"
               x2="29.343447"
               y2="235.1051"
               gradientUnits="userSpaceOnUse" />
          </defs>
          <sodipodi:namedview
             id="base"
             pagecolor="#ffffff"
             bordercolor="#666666"
             borderopacity="1.0"
             inkscape:pageopacity="0.0"
             inkscape:pageshadow="2"
             inkscape:zoom="1"
             inkscape:cx="239.02174"
             inkscape:cy="369.94902"
             inkscape:document-units="px"
             inkscape:current-layer="layer1"
             showgrid="false"
             units="px"
             showguides="false"
             inkscape:window-width="1680"
             inkscape:window-height="987"
             inkscape:window-x="-8"
             inkscape:window-y="-8"
             inkscape:window-maximized="1" />
          <metadata
             id="metadata12921">
            <rdf:RDF>
              <cc:Work
                 rdf:about="">
                <dc:format>image/svg+xml</dc:format>
                <dc:type
                   rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
                <dc:title></dc:title>
              </cc:Work>
            </rdf:RDF>
          </metadata>
          <g
             inkscape:label="Layer 1"
             inkscape:groupmode="layer"
             id="layer1"
             transform="translate(0,-164.70831)">
            <rect
               style="opacity:1;fill:url(#linearGradient15159);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.31061602;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
               id="keyboad-body"
               width="158.75"
               height="132.29167"
               x="0.26458335"
               y="164.44371"
               rx="0.26458335"
               ry="0.26458335"
               inkscape:export-xdpi="72"
               inkscape:export-ydpi="72" />
               
               
               <a href="http://geekgirljoy.wordpress.com" target="_blank" id="go-go-geekgirljoy">
            <g
               id="geekgirljoy-text">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 style="fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0.70712423;stroke-miterlimit:4;stroke-dasharray:none"
                 transform="matrix(1.1225043,0,0,1.1225043,5.0633408,-92.71991)"
                 id="geekgirljoy-bottom">
                <text
                   xml:space="preserve"
                   style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.94135094px;line-height:1.54722679px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0.70712423;stroke-miterlimit:4;stroke-dasharray:none"
                   x="-3.5401669"
                   y="236.54588"
                   id="text5285"><tspan
                     sodipodi:role="line"
                     id="tspan15285-1"
                     x="-3.5401669"
                     y="236.54588"
                     style="fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0.70712423;stroke-miterlimit:4;stroke-dasharray:none">GeekGirlJoy</tspan></text>
                <text
                   id="text5283"
                   y="236.54588"
                   x="-3.5401669"
                   style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.94135094px;line-height:1.54722679px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0.70712423;stroke-miterlimit:4;stroke-dasharray:none"
                   xml:space="preserve"><tspan
                     style="fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0.70712423;stroke-miterlimit:4;stroke-dasharray:none"
                     y="236.54588"
                     x="-3.5401669"
                     id="tspan15307-6"
                     sodipodi:role="line">GeekGirlJoy</tspan></text>
              </g>
              <g
                 id="geekgirljoy-top"
                 transform="matrix(1.1225043,0,0,1.1225043,5.0633408,-92.71991)"
                 style="opacity:0.97000002;fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
                 inkscape:export-xdpi="72"
                 inkscape:export-ydpi="72">
                <text
                   id="text5247"
                   y="236.54588"
                   x="-3.5401669"
                   style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.94135094px;line-height:1.54722679px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
                   xml:space="preserve"><tspan
                     style="fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
                     y="236.54588"
                     x="-3.5401669"
                     id="tspan5245"
                     sodipodi:role="line">GeekGirlJoy</tspan></text>
                <text
                   xml:space="preserve"
                   style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.94135094px;line-height:1.54722679px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
                   x="-3.5401669"
                   y="236.54588"
                   id="text5251"><tspan
                     sodipodi:role="line"
                     id="tspan5249"
                     x="-3.5401669"
                     y="236.54588"
                     style="fill:url(#radialGradient4666);fill-opacity:1;stroke:#003380;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">GeekGirlJoy</tspan></text>
              </g>
            </g>
            </a>
            
            
            <rect
               ry="0.26458335"
               rx="0.26458335"
               y="283.19775"
               x="2.7755576e-017"
               height="13.802217"
               width="158.75"
               id="shading-bottom"
               style="opacity:1;fill:url(#radialGradient4616);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.39234722;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
               inkscape:export-xdpi="72"
               inkscape:export-ydpi="72" />
            <rect
               style="opacity:0.95999995;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#182943;stroke-width:4.23333339;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
               id="keys-background"
               width="96.388542"
               height="95.16375"
               x="30.018394"
               y="189.01839"
               rx="0.26458332"
               ry="0.26458332"
               inkscape:export-xdpi="72"
               inkscape:export-ydpi="72" />
               
            <a href="#" id="Key-B">   
            <g
               id="white-key-b"
               transform="translate(0,-0.56104268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="b-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-80.626374,-672.05687)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="b-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="b-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="b-text"
                 y="276.75943"
                 x="117.51627"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="117.51627"
                   id="tspan4668-6-9"
                   sodipodi:role="line">B</tspan></text>
            </g>
            </a> 
            <g
               id="keyboard-text">
              <text
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="keyboard-bottom"
                 y="179.79259"
                 x="51.96806"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.88270187px;line-height:3.09445357px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f9f9f9;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
                 xml:space="preserve"><tspan
                   style="fill:#f9f9f9;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
                   y="179.79259"
                   x="51.96806"
                   id="tspan15285"
                   sodipodi:role="line">Keyboard</tspan></text>
              <text
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 xml:space="preserve"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.88270187px;line-height:3.09445357px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient4634);fill-opacity:1;stroke:none;stroke-width:0.12377814"
                 x="51.96806"
                 y="179.79259"
                 id="keyboard-top"><tspan
                   sodipodi:role="line"
                   id="tspan15307"
                   x="51.96806"
                   y="179.79259"
                   style="fill:url(#radialGradient4634);fill-opacity:1;stroke-width:0.12377814">Keyboard</tspan></text>
            </g>
            
            <a href="#" id="Key-C" name="Key-C"> 
            <g
               id="white-key-c">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-164.17119,-672.61793)"
                 id="c-rect-group">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19712651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="c-grey-rect"
                   width="3.9700837"
                   height="1.9451796"
                   x="59.26228"
                   y="289.87738"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="c-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="c-text"
                 y="276.7594"
                 x="33.866669"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.7594"
                   x="33.866669"
                   id="tspan4668"
                   sodipodi:role="line">C</tspan></text>
            </g>
            </a> 
            
            <a href="#" id="Key-D"> 
            <g
               id="white-key-d"
               transform="translate(0,-0.14374268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="d-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-150.17727,-672.47417)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="d-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="d-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="d-text"
                 y="276.75943"
                 x="47.997005"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="47.997005"
                   id="tspan4668-6"
                   sodipodi:role="line">D</tspan></text>
            </g>
            </a>
            
            
            <a href="#" id="Key-E"> 
            <g
               id="white-key-e"
               transform="translate(0,-0.19166268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="e-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-136.30317,-672.42625)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="e-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="e-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="e-text"
                 y="276.75943"
                 x="61.623047"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="61.623047"
                   id="tspan4668-6-7"
                   sodipodi:role="line">E</tspan></text>
            </g>
            </a>
            
            <a href="#" id="Key-F"> 
            <g
               id="white-key-f"
               transform="translate(0,-0.33544268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="f-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-122.30925,-672.28247)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="f-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="f-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="f-text"
                 y="276.75943"
                 x="75.976692"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="75.976692"
                   id="tspan4668-6-3"
                   sodipodi:role="line">F</tspan></text>
            </g>
            </a>
            
            <a href="#" id="Key-G"> 
            <g
               id="white-key-g"
               transform="translate(0,-0.36934268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="g-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-108.4944,-672.24857)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="g-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="g-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="g-text"
                 y="276.75943"
                 x="89.272003"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="89.272003"
                   id="tspan4668-6-73"
                   sodipodi:role="line">G</tspan></text>
            </g>
            </a>
            
            <a href="#" id="Key-A"> 
            <g
               id="white-key-a"
               transform="translate(0,-0.51312268)">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 id="a-rect-group"
                 transform="matrix(3.2767822,0,0,3.2767822,-94.50048,-672.10479)">
                <rect
                   style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20480227;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="a-grey-rect"
                   width="3.9700634"
                   height="1.970214"
                   x="59.26228"
                   y="289.85236"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="a-white-rect"
                   width="3.9781194"
                   height="27.495825"
                   x="59.26228"
                   y="262.95197"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="a-text"
                 y="276.75943"
                 x="104.02252"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;stroke-width:0.26458335"
                   y="276.75943"
                   x="104.02252"
                   id="tspan4668-6-6"
                   sodipodi:role="line">A</tspan></text>
            </g>
            </a>
            
            
            <a href="#" id="Key-C-Sharp"> 
            <g
               id="black-key-c-sharp">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-205.38614,-673.39341)"
                 id="c-sharp-rect-group">
                <rect
                   style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79170525;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="c-sharp-grey-rect"
                   width="2.6989212"
                   height="1.2514575"
                   x="74.291664"
                   y="280.27954"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66028059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="c-sharp-black-rect"
                   width="2.6957428"
                   height="17.689808"
                   x="74.291664"
                   y="262.95673"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="c-sharp-text"
                 y="243.509"
                 x="38.438705"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;fill:#ffffff;stroke-width:0.26458335"
                   y="243.509"
                   x="38.438705"
                   id="tspan4668-2"
                   sodipodi:role="line">C#</tspan></text>
            </g>
            </a>
            
            
            <a href="#" id="Key-D-Sharp"> 
            <g
               id="black-key-d-sharp">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-189.99357,-673.39346)"
                 id="d-sharp-rect-group">
                <rect
                   style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79805452;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="d-sharp-grey-rect"
                   width="2.6988971"
                   height="1.2716219"
                   x="74.291664"
                   y="280.25937"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66028059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="d-sharp-black-rect"
                   width="2.6957428"
                   height="17.689808"
                   x="74.291664"
                   y="262.95673"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="d-sharp-text"
                 y="243.509"
                 x="53.773075"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;fill:#ffffff;stroke-width:0.26458335"
                   y="243.509"
                   x="53.773075"
                   id="tspan4668-2-2"
                   sodipodi:role="line">D#</tspan></text>
            </g>
            </a>
            
            
            <a href="#" id="Key-F-Sharp"> 
            <g
               id="black-key-f-sharp">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-163.56119,-673.39346)"
                 id="f-sharp-rect-group">
                <rect
                   style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79805452;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="f-sharp-grey-rect"
                   width="2.6988971"
                   height="1.2716219"
                   x="74.291664"
                   y="280.25937"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66028059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="f-sharp-black-rect"
                   width="2.6957428"
                   height="17.689808"
                   x="74.291664"
                   y="262.95673"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="f-sharp-text"
                 y="243.509"
                 x="80.059036"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;fill:#ffffff;stroke-width:0.26458335"
                   y="243.509"
                   x="80.059036"
                   id="tspan4668-2-1"
                   sodipodi:role="line">F#</tspan></text>
            </g>
            </a>
            
            
            
            <a href="#" id="Key-G-Sharp"> 
            <g
               id="black-key-g-sharp">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-148.92173,-673.39346)"
                 id="g-sharp-rect-group">
                <rect
                   style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79805452;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="g-sharp-grey-rect"
                   width="2.6988971"
                   height="1.2716219"
                   x="74.291664"
                   y="280.25937"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66028059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="g-sharp-black-rect"
                   width="2.6957428"
                   height="17.689808"
                   x="74.291664"
                   y="262.95673"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="g-sharp-text"
                 y="243.10257"
                 x="94.651955"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;fill:#ffffff;stroke-width:0.26458335"
                   y="243.10257"
                   x="94.651955"
                   id="tspan4668-2-4"
                   sodipodi:role="line">G#</tspan></text>
            </g>
            </a>
            
            
            
            <a href="#" id="Key-A-Sharp"> 
            <g
               id="black-key-a-sharp">
              <g
                 inkscape:export-ydpi="72"
                 inkscape:export-xdpi="72"
                 transform="matrix(3.2767822,0,0,3.2767822,-133.19786,-673.39346)"
                 id="a-sharp-rect-group">
                <rect
                   style="opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.79805452;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="a-sharp-grey-rect"
                   width="2.6988971"
                   height="1.2716219"
                   x="74.291664"
                   y="280.25937"
                   rx="0.08074487"
                   ry="0.08074487" />
                <rect
                   style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66028059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
                   id="a-sharp-black-rect"
                   width="2.6957428"
                   height="17.689808"
                   x="74.291664"
                   y="262.95673"
                   rx="0.08074487"
                   ry="0.08074487" />
              </g>
              <text
                 id="a-sharp-text"
                 y="243.509"
                 x="110.64803"
                 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444447px;line-height:6.61458349px;font-family:Pacifico;-inkscape-font-specification:Pacifico;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458335;enable-background:new"
                 xml:space="preserve"><tspan
                   style="font-size:5.64444447px;fill:#ffffff;stroke-width:0.26458335"
                   y="243.509"
                   x="110.64803"
                   id="tspan4668-2-5"
                   sodipodi:role="line">A#</tspan></text>
            </g>
            </a>
            
            
            
            
            <path
               d="m 78.034761,170.34857 -0.718473,-0.10878 -0.67912,0.25852 0.10877,-0.71847 -0.258513,-0.67913 0.718473,0.10878 0.679121,-0.25852 -0.108771,0.71847 z"
               inkscape:randomized="0"
               inkscape:rounded="0"
               inkscape:flatsided="false"
               sodipodi:arg2="1.4640607"
               sodipodi:arg1="0.67866249"
               sodipodi:r2="0.51810318"
               sodipodi:r1="0.99390382"
               sodipodi:cy="169.72464"
               sodipodi:cx="77.261093"
               sodipodi:sides="4"
               id="flare-1"
               style="opacity:0.644;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5174);stroke-width:1.0582726;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;enable-background:new"
               sodipodi:type="star"
               transform="matrix(2.1681284,0,0,0.50590109,-110.80528,84.48127)" />
            <path
               d="m 78.034761,170.34857 -0.718473,-0.10878 -0.67912,0.25852 0.10877,-0.71847 -0.258513,-0.67913 0.718473,0.10878 0.679121,-0.25852 -0.108771,0.71847 z"
               inkscape:randomized="0"
               inkscape:rounded="0"
               inkscape:flatsided="false"
               sodipodi:arg2="1.4640607"
               sodipodi:arg1="0.67866249"
               sodipodi:r2="0.51810318"
               sodipodi:r1="0.99390382"
               sodipodi:cy="169.72464"
               sodipodi:cx="77.261093"
               sodipodi:sides="4"
               id="flare-2"
               style="opacity:0.644;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5146);stroke-width:1.0583334;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;enable-background:new"
               sodipodi:type="star"
               transform="matrix(1,0,0,0.68256474,-14.497777,54.437717)" />
            <path
               d="m 78.034761,170.34857 -0.718473,-0.10878 -0.67912,0.25852 0.10877,-0.71847 -0.258513,-0.67913 0.718473,0.10878 0.679121,-0.25852 -0.108771,0.71847 z"
               inkscape:randomized="0"
               inkscape:rounded="0"
               inkscape:flatsided="false"
               sodipodi:arg2="1.4640607"
               sodipodi:arg1="0.67866249"
               sodipodi:r2="0.51810318"
               sodipodi:r1="0.99390382"
               sodipodi:cy="169.72464"
               sodipodi:cx="77.261093"
               sodipodi:sides="4"
               id="flare-3"
               style="opacity:0.644;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5199);stroke-width:1.0583334;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;enable-background:new"
               sodipodi:type="star"
               transform="matrix(1,0,0,0.68256474,-8.203631,68.866748)" />
            <path
               d="m 78.034761,170.34857 -0.718473,-0.10878 -0.67912,0.25852 0.10877,-0.71847 -0.258513,-0.67913 0.718473,0.10878 0.679121,-0.25852 -0.108771,0.71847 z"
               inkscape:randomized="0"
               inkscape:rounded="0"
               inkscape:flatsided="false"
               sodipodi:arg2="1.4640607"
               sodipodi:arg1="0.67866249"
               sodipodi:r2="0.51810318"
               sodipodi:r1="0.99390382"
               sodipodi:cy="169.72464"
               sodipodi:cx="77.261093"
               sodipodi:sides="4"
               id="flare-4"
               style="opacity:0.644;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5086);stroke-width:1.0583334;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
               sodipodi:type="star"
               transform="matrix(1,0,0,0.68256474,0,53.630995)" />
            <path
               sodipodi:type="star"
               style="opacity:0.644;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient5239);stroke-width:1.0583334;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
               id="flare-5"
               sodipodi:sides="4"
               sodipodi:cx="101.47026"
               sodipodi:cy="169.59991"
               sodipodi:r1="0.99390382"
               sodipodi:r2="0.51810318"
               sodipodi:arg1="0.67866249"
               sodipodi:arg2="1.4640607"
               inkscape:flatsided="false"
               inkscape:rounded="0"
               inkscape:randomized="0"
               d="m 102.24393,170.22384 -0.71847,-0.10877 -0.67912,0.25851 0.10877,-0.71847 -0.25852,-0.67912 0.71848,0.10877 0.67912,-0.25851 -0.10877,0.71847 z"
               transform="matrix(1,0,0,0.74845571,0,42.467279)" />
          </g>
        </svg>

            
        <?php } elseif($method == "png"){ ?>
            
            <img src="keyboard.png" align="middle" usemap="#keyboardmap">

                <map name="keyboardmap">
                  <area id="go-go-geekgirljoy" alt="go-go-geekgirljoy" title="go-go-geekgirljoy" shape="rect" coords="0,0,148,48" target="_blank" href="http://geekgirljoy.wordpress.com" >
                  <area id="Key-C" alt="" title="" href="#" shape="poly" target="_self" coords="115,93,115,433,163,433,164,319,145,318,145,93" />
                  <area id="Key-D" alt="" title="" href="#" shape="poly" target="_self" coords="178,93,179,319,167,319,167,433,217,433,217,319,202,319,203,93" />
                  <area id="Key-E" alt="" title="" href="#" shape="poly" target="_self" coords="236,93,236,319,219,319,220,434,269,434,268,93" />
                  <area id="Key-F" alt="" title="" href="#" shape="poly" target="_self" coords="272,93,272,434,322,434,322,320,303,320,302,93" />
                  <area id="Key-G" alt="" title="" href="#" shape="poly" target="_self" coords="336,93,336,320,324,321,324,434,374,434,374,320,357,319,358,93" />
                  <area id="Key-A" alt="" title="" href="#" shape="poly" target="_self" coords="392,93,392,319,378,319,378,433,428,433,427,319,418,319,418,93" />
                  <area id="Key-B" alt="" title="" href="#" shape="poly" target="_self" coords="451,93,451,319,430,320,430,433,479,433,479,93" />
                  <area id="Key-C-Sharp" alt="" title="" href="#" shape="poly" target="_self" coords="146,93,146,308,178,308,178,93" />
                  <area id="Key-D-Sharp" alt="" title="" href="#" shape="poly" target="_self" coords="204,92,204,308,236,308,236,93" />
                  <area id="Key-F-Sharp" alt="" title="" href="#" shape="poly" target="_self" coords="304,93,304,308,336,308,336,93" />
                  <area id="Key-G-Sharp" alt="" title="" href="#" shape="poly" target="_self" coords="360,92,359,308,392,308,391,93" />
                  <area id="Key-A-Sharp" alt="" title="" href="#" shape="poly" target="_self" coords="419,93,420,309,451,308,450,93" />
                </map>
        <?php } ?>

        </div>


        <script>
          document.getElementById("Key-C").addEventListener("click", function () {document.getElementById("C-Audio").play(); });
          document.getElementById("Key-D").addEventListener("click", function () {document.getElementById("D-Audio").play(); });
          document.getElementById("Key-E").addEventListener("click", function () {document.getElementById("E-Audio").play(); });
          document.getElementById("Key-F").addEventListener("click", function () {document.getElementById("F-Audio").play(); });
          document.getElementById("Key-G").addEventListener("click", function () {document.getElementById("G-Audio").play(); });
          document.getElementById("Key-A").addEventListener("click", function () {document.getElementById("A-Audio").play(); });
          document.getElementById("Key-B").addEventListener("click", function () {document.getElementById("B-Audio").play(); });
          document.getElementById("Key-C-Sharp").addEventListener("click", function () {document.getElementById("C-Sharp-Audio").play(); });
          document.getElementById("Key-D-Sharp").addEventListener("click", function () {document.getElementById("D-Sharp-Audio").play(); });
          document.getElementById("Key-F-Sharp").addEventListener("click", function () {document.getElementById("F-Sharp-Audio").play(); });
          document.getElementById("Key-G-Sharp").addEventListener("click", function () {document.getElementById("G-Sharp-Audio").play(); });
          document.getElementById("Key-A-Sharp").addEventListener("click", function () {document.getElementById("A-Sharp-Audio").play(); });
        </script>


    </body>
</html>

 

If you have any questions please leave them in the comments.

If you would like to get a copy of this entire project’s code and assets you can get a copy over on GitHub here.

Please Like, Comment & Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Are your GIF’s infected?

Have you ever needed to create a GIF and found the available options for doing so less than stellar?

I mean sure, you could try your luck with an option off download.com and risk a virus! However, if you value your electronic devices as well as your data and would like to just skip the absolute hellish nightmare of dealing with the resulting ransomware you might just decide give one of the more reputable cloud build GIF making services a try instead.

However, fail to use one of those more “reputable” build services and you are back to square one, downloading viruses!

Further, even if you are lucky and do find a service that isn’t a front for a black hat syndicate, anyone with an ounce of sense (28.3495 grams to be exact) 😛 will be left with with more questions and concerns than confidence!

Questions like, do I still own my images? or Are the generated GIF’s mine? Have I given away or extended use rights (redistribution or public display) to the site owners? Have they been tampered with, down-scaled or were any watermarks added? Am I compromising my brand or business by using this service? Am I sure I know what I am getting back is safe for myself and my clients?

Right about now a few of you might be thinking “You can’t get a virus from an image since there is no executable code”… but of course you’d be wrong!

So… Let’s take a trip back to 2002 with this article from PC mag. The first jpeg virus had been identified in the wild and while it was benign (both then and certainly now) it demonstrates that even images can be vectors of attack and a means of passive propagation!

The fact is that even if a GIF acts only as a means of delivering a viral payload, a cargo container on which the malware hitches, the GIF (or image in general) is therefore necessarily used in conjunction with a viewer or player application (executable) and therein lies the problem! The image data must be read to be viewed.

The risk posed here is by a so called “zero-day” attack utilizing as yet undocumented vulnerabilities in the viewer app itself, the codec or libraries used to read the media or even potentially the OS, and if you think you are safe because you’d somehow intuit that the image is seemingly “larger than it should be” (in terms of byte size) well, you’d be wrong again!

Remind me to have a talk with you later about this whole doubting me thing… its weird, it’s becoming a problem and it needs to stop! 😛

Appending raw byte data is a real sloppy & amateur way to infect (or store) data in an image!

There are plenty of methods for hiding data in images that will NOT modify the file size!

Further, I have some future stenographic projects planned so I will be brief here but just to prove the point… consider an image that is only 100x100px.

Like this one:

100x100.png

 

Such an image tends to be only a few bytes to kilobytes in size, the image above is only 0.506 KB (damn small) & you could hardly store any data in that right? Well if you are a clever hacker or computer scientist you can come up with quite a few different ways to encode the data directly into the pixel values themselves while at the same time making such slight modifications to the pixels that no perceivable change has occurred!

Additionally, high definition images are common these days so if anything this example is overly critical and in reality you will commonly deal with larger images. The simple fact is you cannot simply look at file size as and indicator for file infection when the file is an image!

Therefore even in a single static JPEG image that is only 100x100px (like above) could store at minimum 10,000 characters (about double all the characters in this entire post)!

Just when you were thinking “WOW TL;DR!” 😛

If you drop fidelity of the image as a concern you can write data to all color channels and that gives you 30,000 (~6X the length of this post) characters to work with! If instead you use a PNG, so that you can play with the alpha channel too, you get 40,000 (~8X the length of this post) characters (assuming that you didn’t use the alpha channel as a checksum ;-))… I can go on for quite some time on this very deep and fascinating subject but as I said I have some stenographic projects planed for you guys in the future and we will cover this stuff in more detail in those posts, so suffice it to say that just because “it’s an image” does not make it safe!

Now, I’m not trying to scare you… okay maybe a little 😛  but I want you to understand that the fact that you upload and download images to cloud build services is in no way a protection and a professional content creator cannot guess that their content is safe to open.

This is where I found myself when I wanted to animate and publish the Sierpinski triangle animations in my post A Chaos Game.

Sierpinski_triangle

That’s why I used the GIFEncoder PHP class released by László Zsid to animate them myself using PHP rather than risk my system with unknown files! The great thing about GIFEncoder is that you don’t have to wonder if it’s safe,  feel free to read the code yourself over on my GitHub and grab a copy while you  are there! 😉

Plus admit it, its just more impressive and satisfying to copy & paste the code I give you and get a GIF rather than images which you then have to go stitch into a GIF yourself! 😛

I also previewed my latest Patreon App called GIFMaker in my last post A Value Proposition. Well today I am pleased to announce that I have made GIFMaker available over on Patreon for my User and Developer level followers!

Here are some screenshots of GIFMaker :

MAIN VIEW

FILE BROWSER

SETTINGS

 

GIFMAKER GALLERY SIGN IN

GIFMAKER GALLERY

The best part is that you can actually see the code that is responsible for creating the GIF as GIFMaker uses a PHP server back-end (either on your local machine or on your web server) so even my User level followers can modify the core functionality of GIFMaker  (and GIFMaker Gallery) to meet their needs!

If you also would like to modify the GIFMaker GUI you can become a Developer level supporter of mine and get access to the C# Source Code and Unity3D IDE Project along with a commercial reuse license should you want or need that!

  • There are no artificial limits in terms of image dimensional size or byte data placed on the application so if you have the memory and storage, feel free to make GIFs as large as you want!

 

  • GIFMaker will not apply any kind of watermarks or other branding or identifiers to the animations so all you will get back is the images you gave it, simply animated!

 

  • Comes with a Web Interface GIFMaker Gallery enabling the Viewing, Downloading and Deletion of your GIFs.

 

GIFMaker is now available over on Patreon and if it looks & sounds like something you need, consider supporting me over on Patreon so I can bring you more great prototypes and tutorials!

Have a great week!

Much Love,

~Joy

A Value Proposition

 

Who am I?

Geek Girl Joy

Hello, my name is Joy.

I have been very fortunate in my career to have had amazing opportunities to work for very large well known companies like The Walt Disney Company, Sparkletts Water, Karen Kane and Citizens of Humanity among other less iconic companies as either as contracted technical support or as a direct employee supporting IT infrastructure and in diverse rolls like Help Desk, Field and Hardware Repair Technician, Senior Systems Administrator, Web Master, Graphics Artist, 3D Printer/Maker to Programmer & Business Owner and everything between! 😛

I make my own Ethernet cables, build phone and data closets all by my lonesome and program neural networks for my amusement! I like working with PHP and other C Family languages like C++, C# etc. and I occasionally work with Python & JavaScript as the need may arise but ultimately depending on the needs of the project, I’m flexible.

What do I do?

My professional experiences have allowed me to develop an in depth knowledge of technology and how businesses leverage both software and hardware systems to achieve their goals.

Throughout my career I have had to develop and deploy off the shelf as well as custom solutions which has allowed me to grow my skill set so that am able to build functional, proof of concept cross-platform software prototypes very rapidly!

Additionally I leverage my years of experience both working with and evaluating technology to provide my opinion in the form of commentary on various products that I like or that I think my followers can benefit from.

Some examples of my technical commentary include: 

 

Some examples of tutorials I have posted include:

 

These works are free and publicly available to anyone and there is no cost to simply follow me to gain access to them, however I do ask that if you like something I create please share it with others on social media.

I do my best to coordinate all my content through this blog and I freely host code over on my GitHub account for my followers as well.

Additionally I have a Patreon profile where my followers can support me financially and they receive a few extra benefits. Before discussing that though I would like to explain why I am using Patreon over other methods of content distribution and supporter payment management.

 

Why Patreon?

I chose Patreon as a community organizing and content delivery platform because it allows me to distribute my software and content in a way that people are already familiar with and comfortable using. Additionally, while their service isn’t free to the content creator, they only charge me based on any generated profits which means that I don’t have to worry about paying them (losing money) while trying to grow my audience.

Of course it is more costly then simply using PayPal and creating a membership site myself but I look at the extra expense as  the cost of using their services. I am willing to pay a slightly higher premium if it allows me to focus on content creation and development. In the end, my supporters benefit the most because they receive more content and of a higher quality.

 

Why support me?

As I said my supporters get access to my paid premium content. This content usually takes the form of expanding on the ideas and projects I blog about to illustrate the concept  using a real world example or prototype.

Some examples of my Prototypes include:

Pinger

 

 

 

Pathfinder Visual Interface

 

 

 

A Geek Sneak Peak 😛

Here is a preview of a currently unreleased project that I will make available on Patreon when it’s ready.

The list of software available to my supporters will continue to grow and reflect their needs and my interests.

I like making functional tools that actually accomplish something or solve real problems, but I can also make games if that is what my audience would prefer.

 

Support Levels

SupporterSupporters – are people who like my public content and want to support my efforts.

 

$1 a month Supporters Enjoy:

  • Access to any  Patreon-only posts, or messages I publish on Patreon
  • Your name listed as a Patreon Supporter on my sponsors page, posts and anywhere else that it is relevant to list and thank my supporters.
  • The satisfaction of knowing you are helping me develop free and awesome software and other content!
  • Access to all the public content I publish here on my blog  & github and any other venue where I publish public content.
  • Suggest projects you would like me to work on.

 

Users – are people who want access to use all the closed source software I create and publish to Patreon. This is software as a service, I am your developer, what can I build to make your world a better place?

 

$5 a month Users Enjoy:

  • All the benefits Supporters enjoy.
  • Access to use the closed source software and content I publish to Patreon.
  • The ability to request features be added to the software  you like.

 

Developers – are people looking to learn to code and build products or simply looking for a prototype. They get access to the closed source code of the software I release on Patreon so they can develop from my prototypes.

$25 a month Developers Enjoy:

  •  All the benefits Supporters & Users enjoy.
  •  Access to use the source of the closed source software and content I publish to Patreon.
  • A  a commercially permissible reuse license.

 

If this sounds good to you and motivates you to want to support me so that I can continue to create projects like these as well as to grow bigger and better, please consider supporting me over on Patreon.

Please Like, Comment & Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

Much Love,
~Joy

A Chaos Game

Hello everyone I hope you had a great week! Mine was too short due to the Independence Day (4th of July) holiday here in the U.S. and to make it worse , FULL of frustrating bug fixes so I am going to keep this weeks tutorial short & simple! 😛

Alright, so this week I have a fun math + animation project for you.

We are going to generate a series of images containing the Sierpinski triangle (a well known fractal) and then animate them using the GIFEncoder class released by László Zsid

The Sierpinski Triangle

Here is a time laps gif of  The Sierpinski Triangle:

Many people (myself included) find fractals incredibly beautiful!

How does this work?

It’s surprisingly simple to create the Sierpinski Triangle and there are many different ways to accomplish our goal however a really simple way is called the “chaos game“, here is the formula :

  1. Select 3 points in the image to form a triangle (ideally it should be an equilateral triangle however based on my experimentation it doesn’t seem to be absolutely required, the triangle will deform (warp) but not entirely disappear).
  2. Randomly select any point inside the points.
  3. Randomly select any one of the 3 vertex points.
  4. Move half the distance from your current position to the selected vertex.
  5. Plot the current position.
  6. Repeat from step 3.

Once we have created all the images and buffered them we create and write the animation to the new gif.

Here is the code for your review:



<?php
set_time_limit(300); // Adjust as needed

include('GIFEncoder.class.php'); // GIFEncoder class


// Buffer Images
// http://php.net/manual/en/function.ob-start.php
// http://php.net/manual/en/function.imagegif.php
// http://php.net/manual/en/function.ob-get-contents.php
// http://php.net/manual/en/function.ob-end-clean.php
function EncodeImage($image, $delay){
	
	global $images, $buffered;

	ob_start(); // Start the buffer
	imagegif($image); // Output image buffer as a gif encoded still
	$images[]=ob_get_contents(); // add the gif still to the images array
	$buffered[]=$delay; // Delay in the animation.
	ob_end_clean(); // Stop the buffer
}


// Build the triangle fractal
// https://en.wikipedia.org/wiki/Sierpinski_triangle
// http://php.net/manual/en/function.imagecolorallocate.php
// http://php.net/manual/en/function.mt-rand.php
// http://php.net/manual/en/function.imagesetpixel.php
function ChaosGame($image, $points){
	global $density,$x, $y;
	
	$counter = 0;
	for ($i = 1; $i < $density; $i++) { // Higher density will plot more points
	
	  $COL = imagecolorallocate($image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); // Random Color
	  imagesetpixel($image, round($x),round($y), $COL); // Plot the current position
	  $a = mt_rand(0, 2); // Randomly select any one of the 3 vertex points for next the iteration
	  
	  // Move half the distance from your current position 
	  // to the selected vertex for next the iteration
	  $x = ($x + $points[$a]['x']) / 2;
	  $y = ($y + $points[$a]['y']) / 2;
	  
	  $counter++;
	  
	  if($counter >= 1000){
		  imagepng($image , "$i.png"); // Save the image 
		  EncodeImage($image, 1); // Buffer image
		  $counter = 0;
	  }
	}

}


$images;
$buffered;

$density = 100000;

$x = 600; // Reset $x
$y = 600; // Reset $y

$inset = 10;
$points[0] = array('x' => $x / 2, 'y' =>  $inset); // Top
$points[1] = array('x' =>   $inset, 'y' => $y - 0); // Left
$points[2] = array('x' => $x - $inset, 'y' => $y - 0); // Right
$image = imagecreatetruecolor($x, $y); // Create the image resource 
ChaosGame($image, $points); // This triangle will rotate
imagedestroy($image); // Free memory


// Generate the animation
$gif = new GIFEncoder($images,$buffered,0,0,0,0,0,'bin');

// Save the gif
$animation_file = fopen('timelaps.gif', 'w');
fwrite($animation_file, $gif->GetAnimation());
fclose($animation_file);

?>

 

 

We can have a little more fun by drawing more than one at a time and animating it like this:

Here is the code:



<?php
set_time_limit(300); // Adjust as needed

include('GIFEncoder.class.php'); // GIFEncoder class


// Buffer Images
// http://php.net/manual/en/function.ob-start.php
// http://php.net/manual/en/function.imagegif.php
// http://php.net/manual/en/function.ob-get-contents.php
// http://php.net/manual/en/function.ob-end-clean.php
function EncodeImage($image, $delay){
	
	global $images, $buffered;

	ob_start(); // Start the buffer
	imagegif($image); // Output image buffer as a gif encoded still
	$images[]=ob_get_contents(); // add the gif still to the images array
	$buffered[]=$delay; // Delay in the animation.
	ob_end_clean(); // Stop the buffer
}


// Build the triangle fractal
// https://en.wikipedia.org/wiki/Sierpinski_triangle
// http://php.net/manual/en/function.imagecolorallocate.php
// http://php.net/manual/en/function.mt-rand.php
// http://php.net/manual/en/function.imagesetpixel.php
function ChaosGame($image, $points){
	global $density,$x, $y;
	
	for ($i = 1; $i < $density; $i++) { // Higher density will plot more points
	  $COL = imagecolorallocate($image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); // Random Color
	  imagesetpixel($image, round($x),round($y), $COL); // Plot the current position
	  $a = mt_rand(0, 2); // Randomly select any one of the 3 vertex points for next the iteration
	  
	  // Move half the distance from your current position 
	  // to the selected vertex for next the iteration
	  $x = ($x + $points[$a]['x']) / 2;
	  $y = ($y + $points[$a]['y']) / 2;
	}
	
}


$images;
$buffered;

$density = 100000;
$frame_increment = 7; // Set to 1 for 360 still images.
                      // At 7 we get 52.

// Create the still images
for ($k = 1; $k < 360; $k+=$frame_increment) {
	$x = 600; // Reset $x
	$y = 600; // Reset $y
	$points[0] = array('x' => $x / 2, 'y' =>  0); // Top
	$points[1] = array('x' =>   10, 'y' => $y - 0); // Left
	$points[2] = array('x' => $x - 10, 'y' => $y - 0); // Right
	$image = imagecreatetruecolor($x, $y); // Create the image resource 
	ChaosGame($image, $points); // This triangle will rotate
    $image = imagerotate($image, $k, 0); // Perform rotation
	ChaosGame($image, $points); // This triangle will not
	imagepng($image , "$k.png"); // Save the image 
	EncodeImage($image, 1); // Buffer image
	imagedestroy($image); // Free memory
}


// Generate the animation
$gif = new GIFEncoder($images,$buffered,0,0,0,0,0,'bin');

// Save the gif
$animation_file = fopen('Sierpinski_triangle.gif', 'w');
fwrite($animation_file, $gif->GetAnimation());
fclose($animation_file);

?>

 

And as previously mentioned, since this post falls during the week of American Independence I would be remiss by not mixing in a little little Red White and Blue, rendered for your desktop wallpaper pleasure (click to get it fullsize):

Here is the code:



<?php
set_time_limit(300); // 5 Minutes Adjust as needed
ini_set('memory_limit', '2G'); // 2 GB Adjust as needed


$images;
$buffered;

$density = 1000000; // Higher density will plot more points but is costly on large images
$x = 4000; // Reset $x
$y = 4000; // Reset $y

$inset = 10;
$points[0] = array('x' => $x / 2, 'y' =>  $inset); // Top
$points[1] = array('x' =>   $inset, 'y' => $y - 0); // Left
$points[2] = array('x' => $x - $inset, 'y' => $y - 0); // Right
$image = imagecreatetruecolor($x, $y); // Create the image resource 

$red = imagecolorallocate($image, 255, 0, 0);
$white = imagecolorallocate($image, 255, 255, 255);
$blue = imagecolorallocate($image, 0, 0, 255);

for ($i = 1; $i < $density; $i++) { // Higher density will plot more points but is costly on large images

  if($x < 4000 / 3){$color = $red;}
  elseif($x > ((4000 / 3)) && $x < ((4000 / 3)*2) ){$color = $white;}
  else{$color = $blue;}

  imagesetpixel($image, round($x),round($y), $color); // Plot the current position
  $a = mt_rand(0, 2); // Randomly select any one of the 3 vertex points for next the iteration
  
  // Move half the distance from your current position 
  // to the selected vertex for next the iteration
  $x = ($x + $points[$a]['x']) / 2;
  $y = ($y + $points[$a]['y']) / 2;
}

imagejpeg($image , "redgreenblue.jpg"); // Save the image 

imagedestroy($image); // Free memory

?>

 

If you would like to obtain a copy of this code from GitHub you can find it here: SierpinskiTriangle Project on GitHub

 

Please support me on Patreon so I can continue to bring you more great tutorials like this!

As always I hope you found this both interesting and informative. Please Like, Comment & Share this post with your friends and followers on your social media platforms and don’t forget to click the follow button over on the top right of this page to get notified when I post something new.

If would like to suggest a topic or project for an upcoming post feel free to contact me.

Much Love,
~Joy

 

 

Pathfinder Visual Interface

Meet Pathfinder’s New Look!

What is Pathfinder?

I recently wrote a tutorial where I introduce the Pathfinder neural network and cover how it works and I provided fully functional code as well as a training set to get you and Pathfinder working from scratch!

You can find that over here if you are interested: Pathfinding From Scratch Using a Neural Network

8 Direction Stepping
8 Direction Stepping

Pathfinder is an example of a neural network that is capable of plotting an 8 direction step path from a starting position in a 5×5 grid to an ending position in that grid.

I donated the Pathfinder ANN example to the Official FANN PHP Repo and you automatically obtain a copy over there for free when you download the FANN PHP extension. 😉

Pathfinder on YouTube

If you have been following me for a little while you may have even seen my YouTube Pathfinder demonstration video. Go ahead and give it a play, it’s kinda fun to watch! 🙂

Note: The experience of the Pathfinder Visual Interface (PVI) application differs slightly from this video as this video demonstrates the pathfinder neural network operating rather than the PVI, however it is quite similar. The main difference is that this example is automated to execute a series of paths whereas the PVI allows you to manually set the start and stop positions one at a time.

 

Well, now I am announcing the release of a Visual Interface for Pathfinder!

 

What’s the Pathfinder Visual Interface (PVI)?

The PVI is a front-end GUI application for Pathfinder and it comes with a slightly customized and streamlined version of my Pathfinder Neural Network.

Instead of having to manually run the training process, Pathfinder will automatically generate its mind file (pathfinder_float.net) for you from the available (and modifiable) training data on your web server, however I do provide you with a fully trained version from the start to work with to make it even easier to get started!

Additionally, the customized version of Pathfinder comes with basic single user salted & hashed password security with session handling so you could improve and expand Pathfinder as needed without worrying about unauthorized access to your web server running Pathfinder.

The Pathfinder Visual Interface was created using Unity 3D and utilizes C# under the hood.

The Pathfinder Visual Interface is compiled for Windows, Mac and Linux, both 32 bit (x86) & 64 bit (x64) executables are available.

I may make WebAssembly as well as Android and iOS versions available if there is enough interest.

What’s the Difference?

Unlike the PVI version of Pathfinder, the open source version of Pathfinder runs in a browser and is strictly text based (stylized with CSS) which looks like this:


Testing Pathfinder:

10000
00000
00000
00000
00001

Results:

10000
01000
00100
00010
00001


Completely functional, however not very nice to look at, and you have to interact with Pathfinder directly from the source code. :-/

Now here is the same example as above but using the PVI:

 

 

 

 

 

Clearly it’s easier to test and work with your Pathfinder Neural Network and dare I say more fun too! 😛

 

What’s Required?

The Pathfinder ANN relies on PHP so you will need a web server like XAMPP if you will be running it on your local machine or a web hosting account that grants you administrative access via SSH (Secure Shell) and or any other system or implementation that allows you to install PHP extensions as an administrator.

If you want to get started quickly you can use c9.io for free (not a sponsor) and don’t want to or are unable to install a web server on 127.0.0.1.

Pathfinder also requires the free (but very powerful) FANN (Fast Artificial Neural Network) Library to be installed on the machine that is serving and supporting Pathfinder.

I do recommend using secure connections so if you are running Pathfinder from your website you should be encrypting your traffic using SSL Certificates (HTTPS) to make sure that the data exchanged with Pathfinder (as well as your password and other sensitive data) is not exchanged as plain text.

Note that If you are a Developer level supporter on Patreon and want to modify the PVI app you will also need to obtain a free copy of Unity 3D however it is not needed if you only want to to use or modify the neural network which is written in PHP.

 

What could you actually do with with the PVI?

The PVI exchanges it’s grid data with the Neural Network running on your web server so you are not limited by local resources and as such Pathfinder and the PVI make excellent starting points to build games as well as robotics & IOT projects.

If you are the kind of person asking questions like how can I deploy a neural network to interface with my game or web capable Arduino or Raspberry Pi project, and you also have some basic web server knowledge, this might be the right project for you! 😉

 

What do I actually get?

If you are looking to bypass the development of an extensible web based pathfinder neural network and would like a cross-platform (Win, Mac, Linux) tool to interface with the ANN for testing you should support me over on Patreon at the User level which is perfect for teachers and students looking to study PHP, Unity 3D or pathfinding using neural networks.

User

For only $5 Users Get:

  • Access to the open source customized PHP based Pathfinder UI edition and the closed source Cross-Platform (Win, Mac, Linux) executable of the Pathfinder Visual Interface application.

 

If you also want to be able to modify the PVI application so that it is customized to your project needs you should support me over on Patreon at the Developer level.

For only $25 Developers Get:

  • Access to the open source customized PHP based Pathfinder UI edition and the closed source Unity 3D project used to create the Cross-Platform Pathfinder Visual Interface application that you can freely modify to meet your project needs.
  • Additionally developers receive a commercially permissible reuse license!

Users may use the Pathfinder Visual Interface (as is) in a private or commercial setting but you may not redistribute resell it or reverse engineer or decompile it unless you are a Developer level supporter Patreon.

If you would like to get yourself a copy of the Pathfinder Visual Interface it is available now on Patreon!

Next week I will be releasing another tutorial so please Like, Comment & Share this post with your friends and followers on your social media platforms and don’t forget to click the follow button to get notified when I post something new.

If would like to suggest a topic or project for an upcoming post feel free to contact me.

Much Love,

~Joy

Create a free website or blog at WordPress.com.

Up ↑