Coding

Another year, another framework.

2022-03-28 

Maybe I’ll flesh this out a bit later, but I’ve just ditched Bludit for a complete rewrite in 11ty.

TOTAL CONTROL. Wicked speed. This is insane. And you know I’m enjoying myself, since I’ve been here since yesterday around noon learning how it works, andporting this fucker from scratch.

This is pretty much rock-bottom — you don’t get much more raw speed than static pages. But with a build-process that’s super easy to get what you want. 🀨

Hopefully this will encourage me to post some more…

Provide Your Own “Jeopardy!” Answers

2021-09-18 

An exceedingly simple recreation of the answer cards seen on Jeopardy!. But it’s worth noting that the modern answer cards aren’t actually a simple blue background, but a surprisingly nuanced barely perceptible gradient. Without it, something looks ‘off’, so hopefully I’ve added just enough here to satisfy. πŸ˜‰

This isn’t the original font, either, but a recreation… supposedly…

Look, it goes down a weird hole and this is just a fun CSS exercise. πŸ˜‰

Anyway, the ‘editable’ part of this is a cheat, using contenteditable attribute on the containing div tag.

X-Day Countdown

2021-04-04 

A couple years ago, I created an Android Wear (now “Wear OS”) watchface (on Google Play) that threw the gaze of “Bob” upon you while you read the time. If you tapped the clock, it would also show you a countdown to that magic hour of 7am on July 5th when you get whisked away just in time for the end of the world.

Wear OS sucks. But I enjoyed working on the watchface. And I wanted some exercise with Svelte. So here it is!

XDayCountdown.com!

I’m going to refine this a bit as time goes on. And I’m sure there’s some subtle (or not so subtle) bugs in there.

There’s no SECRETS yet (or are there?), but I hope to add something fun to help usher in The Moment when it comes. πŸ˜‰

Source, of course, is available over here: https://github.com/Fortyseven/XDayCountdownSite/

Behold Your Vector Nightmare, Amiga Fans

2021-03-06 

Over on Hacker News there was an entry about the “awful” Amiga Kickstart 1.x icon and why it looked the way it did. This led to a link over on Stack Overflow where it was revealed that this was graphic actually drawn in a vector style, as opposed to raster pixels.

They also provided the actual bytes used to render the graphic. I thought it’d be fun to write a little parser to render these bytes.

And so did several other people, apparently, as I’m discovering now. 😐

Anyway, here’s my humble Codepen. And if that goes down, the original code is below the embed. 😎

(I skipped flood fill because I used two.js for this, and didn’t realize until it was too late that I’d chosen poorly. Easy enough to swap out the graphics library, but it’s time to move on.)

See the Pen Amiga Kickstart vector parser by Toby D (@Fortyseven) on CodePen.

/*
    Inspired by:
    https://retrocomputing.stackexchange.com/questions/13897/why-was-the-kickstart-1-x-insert-floppy-graphic-so-bad/13901

    2021-03-06
    \*/

const floppy = [
  0xff, 0x01, 0x23, 0x0b, 0x3a, 0x0b, 0x3a, 0x21, 0x71, 0x21, 0x71, 0x0b, 0x7d,
  0x0b, 0x88, 0x16, 0x88, 0x5e, 0x7f, 0x5e, 0x7f, 0x38, 0x40, 0x38, 0x3e, 0x36,
  0x35, 0x36, 0x34, 0x38, 0x2d, 0x38, 0x2d, 0x41, 0x23, 0x48, 0x23, 0x0b, 0xfe,
  0x02, 0x25, 0x45, 0xff, 0x01, 0x21, 0x48, 0x21, 0x0a, 0x7e, 0x0a, 0x8a, 0x16,
  0x8a, 0x5f, 0x56, 0x5f, 0x56, 0x64, 0x52, 0x6c, 0x4e, 0x71, 0x4a, 0x74, 0x44,
  0x7d, 0x3c, 0x81, 0x3c, 0x8c, 0x0a, 0x8c, 0x0a, 0x6d, 0x09, 0x6d, 0x09, 0x51,
  0x0d, 0x4b, 0x14, 0x45, 0x15, 0x41, 0x19, 0x3a, 0x1e, 0x37, 0x21, 0x36, 0x21,
  0x36, 0x1e, 0x38, 0x1a, 0x3a, 0x16, 0x41, 0x15, 0x45, 0x0e, 0x4b, 0x0a, 0x51,
  0x0a, 0x6c, 0x0b, 0x6d, 0x0b, 0x8b, 0x28, 0x8b, 0x28, 0x76, 0x30, 0x76, 0x34,
  0x72, 0x34, 0x5f, 0x32, 0x5c, 0x32, 0x52, 0x41, 0x45, 0x41, 0x39, 0x3e, 0x37,
  0x3b, 0x37, 0x3e, 0x3a, 0x3e, 0x41, 0x3d, 0x42, 0x36, 0x42, 0x33, 0x3f, 0x2a,
  0x46, 0x1e, 0x4c, 0x12, 0x55, 0x12, 0x54, 0x1e, 0x4b, 0x1a, 0x4a, 0x17, 0x47,
  0x1a, 0x49, 0x1e, 0x4a, 0x21, 0x48, 0xff, 0x01, 0x32, 0x3d, 0x34, 0x36, 0x3c,
  0x37, 0x3d, 0x3a, 0x3d, 0x41, 0x36, 0x41, 0x32, 0x3d, 0xff, 0x01, 0x33, 0x5c,
  0x33, 0x52, 0x42, 0x45, 0x42, 0x39, 0x7d, 0x39, 0x7d, 0x5e, 0x34, 0x5e, 0x33,
  0x5a, 0xff, 0x01, 0x3c, 0x0b, 0x6f, 0x0b, 0x6f, 0x20, 0x3c, 0x20, 0x3c, 0x0b,
  0xff, 0x01, 0x60, 0x0e, 0x6b, 0x0e, 0x6b, 0x1c, 0x60, 0x1c, 0x60, 0x0e, 0xfe,
  0x03, 0x3e, 0x1f, 0xff, 0x01, 0x62, 0x0f, 0x69, 0x0f, 0x69, 0x1b, 0x62, 0x1b,
  0x62, 0x0f, 0xfe, 0x02, 0x63, 0x1a, 0xff, 0x01, 0x2f, 0x39, 0x32, 0x39, 0x32,
  0x3b, 0x2f, 0x3f, 0x2f, 0x39, 0xff, 0x01, 0x29, 0x8b, 0x29, 0x77, 0x30, 0x77,
  0x35, 0x72, 0x35, 0x69, 0x39, 0x6b, 0x41, 0x6b, 0x41, 0x6d, 0x45, 0x72, 0x49,
  0x72, 0x49, 0x74, 0x43, 0x7d, 0x3b, 0x80, 0x3b, 0x8b, 0x29, 0x8b, 0xff, 0x01,
  0x35, 0x5f, 0x35, 0x64, 0x3a, 0x61, 0x35, 0x5f, 0xff, 0x01, 0x39, 0x62, 0x35,
  0x64, 0x35, 0x5f, 0x4a, 0x5f, 0x40, 0x69, 0x3f, 0x69, 0x41, 0x67, 0x3c, 0x62,
  0x39, 0x62, 0xff, 0x01, 0x4e, 0x5f, 0x55, 0x5f, 0x55, 0x64, 0x51, 0x6c, 0x4e,
  0x70, 0x49, 0x71, 0x46, 0x71, 0x43, 0x6d, 0x43, 0x6a, 0x4e, 0x5f, 0xff, 0x01,
  0x44, 0x6a, 0x44, 0x6d, 0x46, 0x70, 0x48, 0x70, 0x4c, 0x6f, 0x4d, 0x6c, 0x49,
  0x69, 0x44, 0x6a, 0xff, 0x01, 0x36, 0x68, 0x3e, 0x6a, 0x40, 0x67, 0x3c, 0x63,
  0x39, 0x63, 0x36, 0x65, 0x36, 0x68, 0xff, 0x01, 0x7e, 0x0b, 0x89, 0x16, 0x89,
  0x5e, 0xfe, 0x01, 0x22, 0x0b, 0xfe, 0x01, 0x3b, 0x0b, 0xfe, 0x01, 0x61, 0x0f,
  0xfe, 0x01, 0x6a, 0x1b, 0xfe, 0x01, 0x70, 0x0f, 0xfe, 0x01, 0x7e, 0x5e, 0xfe,
  0x01, 0x4b, 0x60, 0xfe, 0x01, 0x2e, 0x39, 0xff, 0xff,
];

class AmigaVectParser {
  constructor(bytes, elem) {
    this.palette = ["#FFFFFF", "#000000", "#7777CC", "#BBBBBB"];
    this.offset = [0, 0];
    this.prevOffset = [0, 0];
    this.curColor = 0;
    this.isDrawing = false;
    this.buffer = bytes || [0xff, 0xff];
    this.done = false;

    this.two = new Two({ width: 640, height: 400 }).appendTo(elem);
  }

  doCmd(cmd_pair) {
    if (cmd_pair[0] === 0xff) {
      this.isDrawing = false;
      if (cmd_pair[1] === 0xff) {
        // cmd_done
        this.done = true;
        return;
      } else {
        // cmd_colorSet
        this.curColor = cmd_pair[1];
        return;
      }
    } else if (cmd_pair[0] === 0xfe) {
      // cmd_floodFill
      this.isDrawing = false;
      this.pointer += 2; //TODO FLOOD FILL
      return;
    }
    if (!this.isDrawing) {
      // first coordinate in a poly-line
      this.prevOffset[0] = cmd_pair[0];
      this.prevOffset[1] = cmd_pair[1];
      this.isDrawing = true;
      return;
    } else {
      // continuing the poly-line
      this.offset[0] = cmd_pair[0];
      this.offset[1] = cmd_pair[1];

      let line = this.two.makeLine(
        this.prevOffset[0] * 2,
        this.prevOffset[1] * 2, // doubling up X/Y to make it easier to see at 640x400
        this.offset[0] * 2,
        this.offset[1] * 2
      );

      line.stroke = this.palette[this.curColor];
      line.linewidth = 1;

      this.prevOffset[0] = this.offset[0];
      this.prevOffset[1] = this.offset[1];
    }
  }

  draw() {
    let cmd = [0, 0];
    let pointer = 0;
    this.done = false;

    while (!this.done) {
      cmd[0] = this.buffer[pointer++];
      cmd[1] = this.buffer[pointer++];
      this.doCmd(cmd);
    }
    this.two.update();
  }
}

renderer = new AmigaVectParser(floppy, document.getElementById("draw-shapes"));
renderer.draw();

WordPress Plugins That Changed My Life

2020-12-24 

In my day job, I do a great deal of development on WordPress sites. I’ve come a long way in my understanding of the popular CMS software in the last couple years, but I’m still learning something new all the time.

In that spirit, here’s a collection of WordPress plugins that I’ve found to be life changers. I consider them must-installs when working on new sites:

Advanced Custom Fields (Pro)

I remember when I started creating custom post types and using ACF. It expanded my perception of what a WordPress site could be. Before discovering ACF, I had to resort to ugly hacks and just kind of lumping posts together with categories and duct tape.

The base ACF is free, but the Pro version is not only an extremely fair price (A$25 at the time of writing), but includes invaluable field types, like the Repeater, Options pages, and the ability to integrate with the new Blocks feature in WP5.

WP Updates Notifier

This one may have been abandoned, but it still works as of this writing. It checks for plugin, theme, and core updates regularly, and emails me about it. WordPress being kept updated is crazy important, so it’s great to be able to update as soon as possible. Might need to find a replacement for it, though, if it really has been left for rats.

View Admin As

If you’re doing any work with user roles, you need this. View Admin As allows you to switch between various roles and capabilities without having to log out, or keep a private browsing window open with another account loaded up.

Query Monitor

Which template wound up being loaded? Why is this taking so long to load? WHERE ARE MY PANTS?? Query Monitor adds a wonderful dropdown on the admin bar that helps you find out the answer to all of these, and more.

And these I discovered most recently:

JSM’s Show Post Meta

Dumps all of a post’s meta values at the bottom of it’s edit screen. See every little bit of information being stashed along side your posts. Why something like this isn’t included as a built-in debug tool is beyond me.

Debug Toolkit

Adds the wonderful vd(), vdd() debug methods (substitutes for print_r, et al), and a gorgeous, very helpful error message via the Whoops error handler.

Just a word of warning though: this seems to crank the error reporting level, so even basic issues stop everything in their tracks. Normally, this is good — fix it! But I had at least one time where it caused a silent crash, and I couldn’t do ANYTHING in WordPress. Not even disable Debug Toolkit. I had to go in and manually remove the plugin from the command line. MOST of the time, it’s perfectly great though. Just stay aware.

WP Console

An interactive PHP console via the admin bar! Instead of hacking in a test or two and dying somewhere, just pop down the console and test out your PHP/WordPress code assumptions in a safe space.

The Kansas City Standard

2020-12-20 

(This is a mirror from swtpc.org [archive.org], which itself is a mirror from BYTE Magazine. Minor formatting changes have been introduced.)

BYTE’s Audio Cassette Standards Symposium

Written by Manfred and Virgina Peschke
BYTE, Feb 1976, Pages 72 and 73

BYTE Magazine sponsored a symposium on November 7 and 8, 1975 in Kansas City MO regarding the interchange of data on inexpensive consumer quality audio cassette drives.

These drives may be used as one of the mass storage devices in the first generation of personal computers, and will retain importance for some time to come as a means of interchange of software between computer enthusiasts who purchase products of the small systems industry.

In order to promote the growth of the industry, BYTE sought to achieve an industry standard on audio cassette data interchange through a working conference.

We extend our greatest appreciation to the 18 people who worked very hard until late Friday night and Saturday morning to discuss the multitude of problems and solutions associated with digital recording on auto cassettes. The names of the participants are listed in Table 1.

In spite of the short time available, the participants were able to draft a set of provisional standards which seems to promise great reliability and is rather inexpensive to implement; implementations may be entirely in hardware, or may require a mix of software and some minimal hardware.

Considerations were given to the problems of speed variation among recorders and playback equipment, start and stop delays, recording density (or speed) versus reliability, and recording frequencies to avoid interference with the telephone network in case some users plan to transmit the tones of the cassette over the phone lines.

On Saturday afternoon, Mr. Felsenstein and Mr. Mauch volunteered to write up the consensus among the participants as to a provisional standard which has been reproduced below.

Provisional Audio Cassette Data Interchange Standard
The consensus among the participants of the audio cassette standards symposium at Kansas City MO sponsored by BYTE Magazine is as follows:

  • The proposed standard centers around the use of a frequency shift modulation method from which serial clock data can be extracted at rates of up to 300 baud. The system is intended to be used with low to medium cost cassette recorders incorporating electrical stop and start capability which may be operated under program control.
  • The technique proposed provides for long and short term tape speed variation, limitations in bandwidth due to effects such as tape misalignment, and the necessity to retain low cost and low complexity of the hardware. The technique allows for potential operation at higher tape speed than the nominal 1.875 inch/s (4.75 cm/s).
  • A mark (logical one) bit consists of eight cycles at a frequency of 2400 Hz.
  • A space (logical zero) bit consists of four cycles at a frequency of 1200 Hz.
  • A recorded character consists of a space as a start bit, eight data bits, and two or more marks as stop bits.
  • The interval between characters consists of an unspecified amount of time at the mark frequency. In this respect the data format is similar to that of asynchronous data communication.
  • The eight data bits are organized least significant bit first, most significant bit last, and followed (optionally) by a parity bit. The total number of significant bits and the parity bit cannot exceed 8.
  • Where less than eight data bits are used, the unused bits (following the optional parity bit) at the end of the character are mark bits (2400 Hz).
  • Data will be organized in blocks of arbitrary and optionally variable length, preceded by a minimum of five seconds of marks. To avoid errors due to splice and wrinkle problems common at the beginning of tape, the beginning of the first data block will occur no sooner than 30 seconds from the beginning of clear leader.
  • The contents of the data block are not specified.
  • The data block ends after the stop bits of the final character.
  • Bit clocking information may be extracted from the recorded waveform, which is always an integer multiple of the bit rate, regardless of tape speed. This permits the recovery and retiming of data by means of a UART, which requires a clock of sixteen times the bit rate, although other simple circuitry may be used.
  • A reliable bandwidth of 3000 Hz was assumed in choosing mark and space frequencies due to the head misalignment expected between various cassette recorders. The recording technique is a redundant form of Manchester or bifrequency code which has a long history of reliability in the computer industry. In its present form it was proposed by three independent manufacturers at the conference. One cited reliability rates of one error in 10**7 characters for 200 passes.

Table 1: Participants at Audio Cassette Symposium.

Ray Borrill1218 Prairie Dr, Bloomington IN
Hal ChamberlinThe Computer Hobbyist, P 0 Box 5985, Raleigh NC 27607
Tom DurstonMITS, 6328 Linn NE, Albuquerque NM
Lee FelsensteinLGC Engineering, 1807 Delaware St, Berkeley CA 94703
Joe FrappierMikra-D, 32 Maple St, Bellingham MA
Bill GatesMITS
Gary KaySouthwest Technical Products Corp, 219 W Rhapsody, San Antonio TX 78216
Bob MarshProcessor Technology, 2465 Fourth St, Berkeley CA 94710
Harold A MauchPronetics, 4021 Windsor, Garland TX 75042
Bob NelsonPCM, San Ramon CA
George PerrineHAL Communications Corp, Box 365, Urbana IL 61801
Ed RobertsMITS
Richard SmithThe Computer Hobbyist, P 0 Box 5882, Raleigh NC 27607
Les SolomonPopular Electronics, 1 Park Av, New York NY 10016
Michael StolowitzGodbout Electronics, Box 2355, Oakland Airport CA 94614
Paul TuckerHAL Communications Corp
Mike WiseSphere, 791 S 500 W, Bountiful UT 84010
Bob ZallerMITS

Prenatal ACF Data Insertion (…or “that’s the most boring title I’ve ever written.”)

2020-02-02 

Just wanted to document an interesting issue I had with Advanced Custom Fields recently…

Don’t get me wrong, I swear by ACF on all of my WordPress projects. I consider it an absolute necessity. It’s powerful, easy to use, and is priced reasonably.

It’s when you go outside the standard usage of ACF that things get a bit… thorny…

Into the weeds

Normally, you’d create a field group. And you’d assign that field group based on a set of criteria (a particular page template, or a custom post type, etc.).

Then you create a new post, and you’re presented with your custom fields. You enter your data, and it’s attached to the post when you save. And accessing that data is trivial from templates.

But what happens if you create a post with custom fields, but you’re doing it from inside a PHP function, using wp_insert_post?

What if you want to add content to a field in that post immediately afterward? Surprise! Your standard update_field or add_row code will silently fail. According to the documentation, you need to reference the field name using the field key in that situation.

The field’s key should be used when saving a new value to a post (when no value exists). This helps ACF create the correct β€˜reference’ between the value and the field’s settings.

Let’s unpack that

ACF stores all of it’s custom field values inside standard WordPress meta fields inside the post. In fact, you could just as easily use get_post_meta to retrieve ACF field values under many circumstances. Or even write it back.

But ACF is much more than just a key/value pair, of course. Each field has a whole host of information associated with it. Label name, conditional display logic, field type, etc.

In the post’s meta data, ACF creates two different values: the field name, and a field key reference.

Let’s say I have a custom, basic Text-type field called “Title”.

Inside the post, there will be a title meta data field; this holds the actual value of the field. And then there’s a _title field. The underscore means it’s a field key reference. The value of that looks something like field_123456. Each field group entry gets it’s own unique field key name that looks like that.

Internally, when you call get_field('title') ACF looks up the post meta with an underscore — _title — and uses that to pull up the details in the custom field group entry.

If you call get_field('field_123456'), in fact, it will work as well. ACF will reference the field group info, and return the appropriate post meta that way.

Both are valid ways to work with ACF field content.

A brand new post, inserted with wp_insert_post is completely blank. It has no post meta data, outside of the usual timestamp and creation info.

So if you try to run update_field('title', 'My Title', 9999), it does nothing. As if it doesn’t exist. Because as far as ACF is concerned, it doesn’t.

Not yet.

There’s no _title for it to reference for guidance.

But if you update_field('field_123456', 'My Title', 9999), it WILL work. ACF knows right where to go to get it’s field details, and it works as normal.

Now here’s where it gets tricky

That’s all well and good for a simple Text field type.

But what if I have something more complicated? What if I have a Group type, with a Repeater inside that?

Let’s say I have a Group called “Vehicles” and a Repeater inside that called “Trucks”. (And presumably a “Cars”, “Motorcycles”, etc, but let’s keep it simple!)

And each row inside “Trucks” has a “Model” Text field, and a “Mileage” Number field.

Under normal circumstances I could do:

add_row('vehicles_trucks', ['model'=>'Bigfoot', 'mileage' => '50000'], 9999).

(Note the special, barely documented vehicles_trucks underscore selector notation for these nested fields.)

But if I’ve just inserted the post, none of those key field references exist!

The vehicles_trucks selector doesn’t work. But the previous fix, using the raw key field reference… say, add_row('field_902100'..., well, that doesn’t work either! Because which field key reference makes sense in this situation? The one for Vehicles? The one for Trucks?

If you use the key_ field key for Vehicle, it fails. Vehicle is a Group type. Nothing happens.

If you use the key_ field key for Trucks, however, something weird happens. Instead of creating a _vehicles_trucks key reference, it creates a _trucks reference…

At this point it’s important to note that ACF is smart and slick… right up until the point it is not.

From what I’ve discovered, there is no shortcut to adding a new row to a Repeater field nested inside a Group if you’ve created the post inside PHP, before someone had a chance to hit ‘Save’ on it from the admin.

If you try to get clever, you might fairly think that underscore notation might apply here. Maybe stick the two together, like field_111111_ field_22222.

But you’d be wrong

No, we have to manually create all of ACF’s key references ourselves before we can do anything:

 update_post_meta(9999, '_vehicle', 'field_111111');
 update_post_meta(9999, '_vehicle_truck', 'field_222222');

THEN we can add_row('vehicles_trucks', ... and insert our data programmatically as expected.

This holds true for even deeper nested content, but at that point maybe you want to rethink what you’re doing. πŸ˜‰

I was surprised by just how little information about this specific use case exists. Hopefully this helps somebody out there!

Hit me up on Twitter if I’ve made an error anywhere in here.

Sectional PHP Highlighting in VSCode

2019-07-16 

So, my eyes were glazing over some WordPress PHP in my editor of choice, VSCode.

The weaving in and out of <?php, <?= and ?> tags, bouncing between PHP and HTML contexts was making my head spin.

I kept having fantasies of a time when I could highlight the PHP (or HTML) context using a different background color… but there didn’t seem to be anything like that for VSCode, at least. It’s possible I was thinking of PHPStorm, but that was a lifetime ago.

So I powered through, cleaning up the twisty templates, trying to make them as readable as possible. But I finally hit my breaking point and started looking around… and I think I found a good solution. Whether it’s a good long term solution, well, that remains to be seen.

The Highlighter extension for VSCode is where the magic happens.

This extension allows you to define custom highlighting rules to regex matches in your code.

You can probably see where this is going.

After some futzing around, I wound up with these rules in my VSCode settings config:

"highlight.regexes": {
&lt;a href="https://clips.twitch.tv/JoyousEnthusiasticSpaghettiVoHiYo"&gt;https://clips.twitch.tv/JoyousEnthusiasticSpaghettiVoHiYo&lt;/a&gt;    "(&lt;\?php)((.|r|n)+?)(\?&gt;)": [
        { "color": "#FF00FF", "backgroundColor": "#FF00FF40" },
        { "backgroundColor": "#FF00FF40" },
        { "color": "#FF00FF", "backgroundColor": "#FF00FF40" }
    ],
    "(&lt;\?=)((.|r|n)+?)(\?&gt;)": [
        { "color": "#FF00FF", "backgroundColor": "#FF00FF40" },
        { "backgroundColor": "#FF00FF40" },
        { "color": "#FF00FF", "backgroundColor": "#FF00FF40" }
    ]
}

Your color preferences will vary, of course, but do at least note that I’m using the extended alpha value in the hex (the final octet), so you can blend your background color into your theme’s existing color!

(And yes, you can combine that down into one regex. Go ahead and do that.)

(IMAGE MISSING; SORRY…)

(The rainbow colored indents are part of the terrific indent-rainbow extension!)

I haven’t bounced on this very hard, so you might find some quirks here and there.

For instance, if you don’t include the closing PHP tag, ?>, it won’t match the regex. You could make the closing tag optional, but that might be undesirable…

But in any event, this seems to get me 9/10th of the way to the functionality I want, so I’m pretty happy. 😏

Reincarnation

2019-07-15 

I don’t have a link to it, but I remember reading about how some people essentially “reinvent” themselves multiple times over the course of their lives. They are constantly learning new things and switch careers to some other focus every decade or so, in an attempt to live a rich, varied life free of stagnation.

An interesting idea, if you can pull it off. After all, typically, people are married and have a family to take care of. And if not for a family, one’s own finances need to be secure.

Now, I’m not planning on quitting my job any time soon, mind you (I love it quite a bit), but I think the basic idea could at least be applied to one’s hobbies…

Since I was in high school in the 90s, I’ve always had a thick interest in game development. Skipping past the boring self-analysis, I came close to doing it professionally a couple times in the last decade, but otherwise it’s mostly stayed a hobby. But it was one I actively participated in during my off hours… I could cite various reasons, but suffice it to say that despite my dreams of ‘going pro’, it never took off.

Other, recent events have soured the milk on game development even further. It was probably for the best, though. All it ever did was remind me of unfinished projects, and planning for a future that wasn’t going to exist. Never mind the increasing number of horror stories from inside the industry, as people begin to feel safe about opening up about corporate abuse and general misery.

So, over the last couple months I’ve decided to pack up my game development hobby and put it into a little box in the closet. Sure, I’ll still keep tabs on industry news and people’s fun indie projects and stuff, but it’s no longer a primary interest.

What will fill the void?

Well, over the last couple years I’ve been, off and on, attending the B-Sides information security conferences along the east coast. I always had fun, but felt a bit weird going to them. It wasn’t my field. I felt like an outsider, even though it was stuff I could potentially apply to my day job. But as time went on, the wheels of further interest started turning…

Network security has always been a major weak point in my computer education. Compiler internals, hardware, software development? Sure, I love that stuff. But network administration? Server security? Subnet masks? OSI layers? I’ve had, more or less, only a scattered, surface level understanding. (No worries — I had a good handle on what to do, and what not to do, when it comes to security when working on software projects, so no worries there at least …mostly. I mean, as far as I know. Oh god, now I’m paranoid.)

So, I’ve been taking courses. I’m going all-in on educating myself about all of it. Taking part in CTF challenges. Pentesting my own internal network. Breaking into vulnerable virtual machines. (Already taught me a ton about WordPress security. Cough.) And I’ve been taking extensive notes as I go.

And you know what? I’m addicted. This is seriously fulfilling stuff. And my interest has only increased the further in I get. It’s like an infinite box of puzzles that keeps my brain active.

So now I have a primary hobby that is not only good for me, good for helping others, but also helps my day job.

I don’t want to say it’s goodbye forever to game development, but it’s going to be a long time, if ever, before that flame is reignited. And hey, maybe I’ll write up some more educational stuff here and there to help others, like me, along the way. I’d be down for that. 😎

Pico-8 – Lines Screensaver

2019-01-22 

Okay, so it’s not really a screensaver in this instance, but it’s the art from inside one. You recognize it. You’ve seen it before. But here it is… much lower res than usual. πŸ˜‰

Source: https://gist.github.com/Fortyseven/a4e2d5d3105ed86e0916d162a1a4d466

pico-8 cartridge // http://www.pico-8.com
version 16
__lua__
-- lines (classic)
-- by fortyseven

SPACING = 4
SPEED = 1

function buildPolyVerts()
    local verts = {
        x=rnd(128),
        y=rnd(128),
        xvel= SPEED,
        yvel= SPEED
    };
    if (rnd(1) > 0.5)  verts.xvel = -verts.xvel
    if (rnd(1) > 0.5)  verts.yvel = -verts.yvel
    return verts;
end;

-------------------------------------------------------------
poly = {}

function poly:init(i, color)
    self.color = color
    self.verts = {}

    -- ensure we use the same seed for each poly object, so it will always
    -- create the same intial x/y/velocity directions

    for i = 1, 4 do
        verts = buildPolyVerts()
        add(self.verts,verts)
    end

    -- run the update a couple times to space them out

    for i = 1, i*SPACING do self:update() end
end

function poly:update()
    foreach(self.verts, function(vert)
        vert.x += vert.xvel
        if ((vert.x >= 128) or (vert.x <= 0)) vert.xvel = -vert.xvel
        vert.y += vert.yvel
        if ((vert.y >= 128) or (vert.y <= 0)) vert.yvel = -vert.yvel
    end)
end

function poly:draw()
    for i = 1, 3 do
        color(self.color);
        line(self.verts[i].x, self.verts[i].y, self.verts[i+1].x, self.verts[i+1].y)
    end
    line(self.verts[4].x, self.verts[4].y, self.verts[1].x, self.verts[1].y)
end

function poly:new(o)
    self.__index = self
    return setmetatable(o or {}, self)
end

-------------------------------------------------------------
polyCluster = {}

function polyCluster:init(col_array)
    self.objects = {}
    seed = flr(rnd(1000))

    -- generate three unique polys
    for i = 1, 3 do
        srand(seed)
        local o = poly:new()
        o:init(i, col_array[i])
        add(self.objects, o )
    end
end

function polyCluster:update()
    foreach(self.objects, function(obj)
        obj:update()
    end)
end

function polyCluster:draw()
    foreach(self.objects, function(obj)
        obj:draw()
    end)
end

function polyCluster:new(o)
    self.__index = self
    return setmetatable(o or {}, self)
end

-------------------------------------------------------------
clusters = {}

function _init()
    local c = polyCluster:new()
    c:init({2,8,14})
    add(clusters, c)

    c = polyCluster:new()
    c:init({1,6,12})
    add(clusters, c)

    c = polyCluster:new()
    c:init({13,6,7})
    add(clusters, c)
end

function _draw()
    foreach(clusters, function(c)
        c:update()
    end)
end

function _update()
    cls()
    foreach(clusters, function(c)
        c:draw()
    end)
end