Logo-i-zer Source

By Joe Crawford. December 2024.

20241112-header.gif

This is a GIF images with ONLY 3 colors. The first two indexed colors are recolored. The third is transparent.

20241112-header.gif

logo-i-zer.php


<?php

// This is an image with 3 colors. 3rd color is transparent.
// GIFs have an indexed color palette which is why this works.
// if you change out the image, and it is a GIF with 3 colors,
// it ought to work.
// This source code is provided without warranty tho.
// https://apps.artlung.com/logo-i-zer/create/

$reference_file '20241112-header.gif';

// if there's no file to work from, don't move on
if (!file_exists($reference_file)) {
    die(
"File not found: $reference_file");
}

// we expect 2 parameters and 2 only
$c0 $_GET['c0'] ?? '000000';
$c1 $_GET['c1'] ?? 'FFFFFF';

// if the colors are in the form #FFFFFF, strip the #
if (strlen($c0) === || strlen($c1) === 7) {
    
$c0 substr($c01);
    
$c1 substr($c11);
}

// if colors are provided in the 3 letter form, expand them
if (strlen($c0) === 3) {
    
$c0 str_repeat(substr($c001), 2)
        . 
str_repeat(substr($c011), 2)
        . 
str_repeat(substr($c021), 2);
}
if (
strlen($c1) === 3) {
    
$c1 str_repeat(substr($c101), 2)
        . 
str_repeat(substr($c111), 2)
        . 
str_repeat(substr($c121), 2);
}

// now we have 6 character hex colors, but if invalid hex numbers
// got through, we have to bail out
$regex '/^[0-9a-fA-F]{6}$/';
if (!
preg_match($regex$c0) || !preg_match($regex$c1)) {
    
http_response_code(400);
    die(
"Invalid color");
}
// let's not put up with any mishagosh with extra parameters
if (count($_GET) > 2) {
    
http_response_code(400);
    die(
"Invalid parameters");
}

// enforce uppercase hex colors.
$c0 strtoupper($c0);
$c1 strtoupper($c1);

$filename =sprintf('images/20241112-header-%s-%s.gif'str_replace('#'''$c0), str_replace('#'''$c1));

if (
file_exists($filename)) {
    
header('Content-Type: image/gif');
    
readfile($filename);
    exit;
}

$im imagecreatefromgif($reference_file);

list(
$r0$g0$b0) = sscanf($c0"%02x%02x%02x");
list(
$r1$g1$b1) = sscanf($c1"%02x%02x%02x");

imagecolorallocate($im$r0$g0$b0);
imagecolorallocate($im$r1$g1$b1);

imagecolorset($im0$r0$g0$b0);
imagecolorset($im1$r1$g1$b1);

$written imagegif($im$filename);

if (!
$written) {
    
http_response_code(500);
    die(
"Failed to write image to images/ directory");
}

$latest_data = [
    
'filename' => $filename,
    
'timestamp' => time(),
];
$written file_put_contents('latest.json'json_encode($latest_data));
if (!
$written) {
    
http_response_code(500);
    die(
"Failed to write latest.json");
}

header('Content-Type: image/gif');
readfile($filename);
exit;

create.php


<!DOCTYPE html>
<html>
<head>
    <title>ARTLUNG LOGO-I-ZER</title>
    <link rel="webmention" href="https://webmention.io/artlung.com/webmention" />
    <meta property="og:image" content="https://apps.artlung.com/logo-i-zer/logo-I-zer.jpg" />
    <meta property="og:description" content="Joe Crawford made this tool to make his ARTLUNG logo with any 2 colors" />
    <style><?php print file_get_contents('create.css'); ?></style>
</head>
<body>
<form method="get" action="./">
    <div>
        <label for="c1">Inner Color</label>    <input type="color" id="c1" value="#FFFFFF" name="c1">
        <label for="c0">Outer Color</label>    <input type="color" id="c0" value="#000000" name="c0">
        <button id="flop-colors">Flop Colors</button>
    </div>
    <button id="create">Create</button>
    <a href="./images/" target="_blank"><img src="images/20241112-header-000000-FFFFFF.gif" alt="ARTLUNG"></a>
</form>
<a href="source.php">Source Code</a>
<script src="create.js?<?php
    
echo '?' filemtime('create.js');
?>"></script>
</body>
</html>

create.scss (create.css)

:root {
  --c0: #000000;
  --c1: #FFFFFF;
}

html {
  height: 100%;
}

body {
  height: 100%;
  font-family: "Source Code Pro", "SF Mono", Monaco, Inconsolata, "Fira Mono", "Droid Sans Mono", monospace, monospace;
  display: grid;
  grid-template-rows: 90vh 10vh;
  align-items: center;
  background-color: color-mix(in srgb, var(--c1) 5%, transparent 80%);
  background-image: linear-gradient(
                  45deg,
                  var(--c0) 2rem,
                  transparent 0
  ),
  linear-gradient(
                  -45deg,
                  var(--c0) 2rem,
                  transparent 0
  ),
  linear-gradient(
                  135deg,
                  var(--c0) 2rem,
                  transparent 0
  ),
  linear-gradient(
                  -135deg,
                  var(--c0) 2rem,
                  transparent 0
  );
  overflow: hidden;
  > a {
    color: var(--c0);
    display: block;
    padding: 1rem;
    text-decoration: underline;
    text-decoration-style: wavy;
    width: min-content;
    white-space: nowrap;
    margin: 0 auto;
  }
}

img {
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

form {
  div {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;

    label {
      text-align: end;
    }

    #flop-colors {
      grid-column: 2;
      font-size: 0.66rem;
      padding: 0.33rem;
      font-weight: normal;
      width: min-content;
      white-space: nowrap;
    }
  }

  display: flex;
  flex-direction: column;
  width: clamp(220px, 50%, 500px);
  margin: 0 auto;
  gap: 1rem;

  button {
    font-family: "Source Code Pro", "SF Mono", Monaco, Inconsolata, "Fira Mono", "Droid Sans Mono", monospace, monospace;
    color: var(--c0);
    background-color: color-mix(in srgb, var(--c1) 5%, transparent 70%);
    border: 3px double var(--c0);
    border-radius: 1rem;
    padding: 1rem;
    font-size: 1rem;
    cursor: pointer;
    font-weight: bold;
  }

  a {
    text-decoration: none;
    border: none;
  }

}

create.js

document.getElementById('create').addEventListener('click', function (evt) {
    console.log('create');
    var c0 = document.getElementById('c0').value;
    var c1 = document.getElementById('c1').value;

    // set value of --c0 and --c1 in the :root style
    document.documentElement.style.setProperty('--c0', c0);
    document.documentElement.style.setProperty('--c1', c1);

    var img = document.querySelector('img');

    c0 = c0.toUpperCase().replace('#', '');
    c1 = c1.toUpperCase().replace('#', '');
    img.src = './?c0=' + c0 + '&c1=' + c1;

    evt.preventDefault();
});


document.getElementById('flop-colors').addEventListener('click', function (evt) {
    console.log('flop colors');
    var c0 = document.getElementById('c0').value;
    var c1 = document.getElementById('c1').value;
    document.getElementById('c0').value = c1;
    document.getElementById('c1').value = c0;
    saveColorsToLocal();
    evt.preventDefault();
});

function saveColorsToLocal() {
    var c0 = document.getElementById('c0').value;
    var c1 = document.getElementById('c1').value;
    localStorage.setItem('c0', c0);
    localStorage.setItem('c1', c1);
}

setInterval(saveColorsToLocal, 1000);


if (localStorage.getItem('c0') && localStorage.getItem('c1')) {
    document.getElementById('c0').value = localStorage.getItem('c0');
    document.getElementById('c1').value = localStorage.getItem('c1');
    document.getElementById('create').click();
}

index.php


<?php
require 'logo-i-zer.php';

images/images.php


<!DOCTYPE html>
<html>
<head>
    <title>ARTLUNG Logo-i-zer Images</title>
    <link rel="webmention" href="https://webmention.io/artlung.com/webmention" />
    <style><?php print file_get_contents('images.css'); ?></style>
    <style>section img { aspect-ratio: 211 / 41; }</style>
    <script>let latest = <?php print file_get_contents('../latest.json'); ?></script>
    <script src="images.js<?php
        
echo '?' filemtime('images.js');
    
?>"></script>
</head>
<body><section><?php
require 'glob.php';
?></section></body></html>

images/glob.php


<?php

$gifs 
glob('*.gif');

rsort($gifs);

foreach (
$gifs as $gif) {
    
printf('<img src="%s" alt="%s" />'$gif$gif);
}

images/index.php


<?php
require 'images.php';

images/images.js

if (latest && latest.timestamp) {
    let lastSuccess = new Date().getTime();
    interval = setInterval(() => {
        fetch('../latest.json')
            .then(response => response.json())
            .then(data => {
                console.log(data)
                if (latest.timestamp !== data.timestamp) {
                    latest = data;
                    const img = document.createElement('img');
                    img.src = latest.filename.replace('images/', '');
                    img.alt = latest.filename.replace('images/', '');
                    img.className = 'fresh';
                    document.querySelector('section').appendChild(img);
                    lastSuccess = new Date().getTime();
                }
            });
        // don't do this forever
        if (new Date().getTime() - lastSuccess > 600000) {
            console.log('stopping because it has been 10 minutes since last success');
            clearInterval(interval);
        }
    }, 4000);
}

// attach a a mouseover to any image that might get added to the body with img.fresh class
// and when mouseover, remove the class

document.addEventListener('mouseover', (e) => {
    if (e.target.classList.contains('fresh')) {
        e.target.classList.remove('fresh');
    }
});

images/images.scss (images/images.css)

html, body {
  height: 100%;
}
body {
  background: conic-gradient(
                  #eee 25%,
                  #fff 0 50%,
                  #eee 0 75%,
                  #fff 0 100%
  ) 0 0 / 50px 50px;
  margin: 0;
  padding: 10px;
  display: grid;
  justify-content: center;
  align-items: center;

  section {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: start;
    align-items: center;
    height: min-content;
    img {
      &.fresh {
        outline: var(--outline-width) solid var(--outline-color);
        animation: pulse 1.2s infinite linear;
      }
    }
  }

}

:root {
  --outline-color: pink;
  --outline-width: 0.5rem;
}
@keyframes pulse {
  0% {
    outline-color: var(--outline-color);
    outline-width: var(--outline-width);
  }
  50% {
    outline-color: transparent;
    outline-width: 0;
  }
  100% {
    outline-color: var(--outline-color);
    outline-width: var(--outline-width);
  }
}

create/index.php


<?php
header
('Location: /logo-i-zer/create.php');