Personal Webpage of Blake Watson https://blakewatson.com The personal webpage of Blake Watson Sun, 11 Aug 2019 21:58:36 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.1 How to: array reduce in JavaScript and PHP https://blakewatson.com/journal/how-to-array-reduce-in-javascript-and-php/ https://blakewatson.com/journal/how-to-array-reduce-in-javascript-and-php/#respond Tue, 25 Jun 2019 04:53:06 +0000 https://blakewatson.com/?p=246 If you make websites, chances are that you work with arrays. A lot. They’re everywhere—a list of posts, a list of followers, a list of links. But working with arrays can sometimes feel a little cumbersome. Sometimes it can take more code than it seems like it should to process an array. Sometimes it can feel… hacky.

You may have heard other developers say, “Just use a reducer.” Or, “a reducer could do that!” Or maybe even, “Reduce is cool and you have to be a functional programming genius like me to understand it.”

Not true, I say! I know next to nothing about functional programming (so far). But I was able to understand and start using reduce functions in my own work. And so can you!

Arrays, the conventional way

Before we look at the power of the reduce function, let’s look at some of the other ways you might access an array. In JavaScript, for example, you are probably familiar with something like:

var fruits = ['orange', 'apple', 'banana'];
for(let i = 0; i < fruits.length; i++) {
    // do something with fruits[i]
}

The above code shows a for loop that is being used to iterate over each element in the fruits array. Really, all it is doing is incrementing a counter, which you can then use to access your array.

You might even be aware of the nicer way of doing this, using an array method:

var fruits = ['orange', 'apple', 'banana'];
fruits.forEach(fruit => {
    // do something with fruit
});

The forEach method accepts a callback function, and runs that function over each element in the array.

This is the first important point to understanding a reducer. The idea of writing a callback function that gets run for each element in the array. Each time it runs, it takes an array element as its argument. So just hang on to that for a second.

List operations

Here’s another functional programming term for you. List operations just refers to methods that run against arrays (lists). Most of the time, they iterate over the array, do things, and return another array as the result.

As a quick example, take the filter function:

var fruits = ['orange', 'apple', 'banana'];
var fruitsILike = fruits.filter(fruit => fruit === 'banana');

The filter function runs a callback function over each element in the array. The callback function must return true or false, depending on whether you want the current element to be included in the final array. What I just called the “final array” is what is returned by filter—it’s a new array containing all of the elements for which you returned true. In this case, an array containing just one element, banana.

Another useful list operation is the map. Let’s look at this one in PHP.

$fruits = [ 'orange', 'apple', 'banana' ];
$fruit_initials = array_map( function( $fruit ) {
    return substr( $fruit, 0, 1 );
}, $fruits );

// Expected return value: [ 'o', 'a', 'b' ];

The array_map function iterates over each element in the array. It returns a new array with the same number of elements as the original, but with each element being whatever you chose to return. Each element in the old array maps to an element in the new one.

Enter the reducer

Now that we have looked at other list operations, we have the basis for understanding the reducer. Before I show you a code sample, consider the following metaphor.

Imagine you have a piece of paper with a list of numbers. You have a calculator which you use to add up all the numbers. As you are adding up the numbers, you have a running total. As you progress down your list of numbers, you simply keep adding each number to the running total.

This is very similar to how a reducer works. You have a list of things (the array) that you are iterating over. As you do, you have access to a variable that represents the current element. Just like we have seen earlier. But you also have a variable that represents the “running total,” if you will. We’ll call it, “carry.” Let’s take a look at this in JavaScript:

var numbers = [42, 7, 99, 2, 33];
var total = numbers.reduce((carry, num) => carry + num, 0);

In the code above, we are supplying reduce two arguments:

  1. Our callback function, which should return the new running total at the end of each iteration.
  2. The initial value of our running total, which in this case we want to be 0.

Okay, so we’re iterating over the numbers array. The reduce function expects our callback function to return the new running total for each iteration. That’s the carry + num bit1. If we were to console.log() the value of carry at the beginning of each iteration, it would look like this:

0
42
49
148
150

And the final total would be 183.

Here’s the same reducer in PHP:

$numbers = [42, 7, 99, 2, 33];

$total = array_reduce( $numbers, function($carry, $num) {
    return $carry + $num;
}, 0 );

So I can add some numbers together? Big deal…

Okay, now this is where reducers get their reputation for being all-powerful. We’ve been thinking of the carry value as being a running total. But here’s the thing—your callback function can return any type of value, and whatever value you return will be passed to your callback function on the next iteration.

Let’s look at a contrived example to see how you can use a reducer for something that has nothing to do with adding numbers together.

Let’s say we have one array that contains fruits and vegetables. Instead we would like to have an object that looks like this:

{
    fruits: [], // array containing only fruits
    veggies: [] // array containing only veggies
}

This is how we could do it with a reducer:

var food = ['apple', 'okra', 'banana', 'spinach'];

var organizedFood = food.reduce((carry, item) => {
    if(item === 'apple' || item === 'banana') {
        carry.fruits.push(item);
    } else {
        carry.veggies.push(item);
    }
    
    return carry;
}, { fruits: [], veggies: [] });

Let’s set aside the fact that this function doesn’t scale very well (it can’t handle any other fruits except apples and bananas). It does show how we can use a reducer to keep a running value of any type. Let’s step through the code.

We pass reduce our callback function. We also pass it the initial value. In our case, we want the initial value to be an empty object with the properties fruits and veggies. We set the value of both of those properties to an empty array.

Our callback function looks at each element in the array. If it matches one of the fruits, we push that element to the carry.fruits array. If it doesn’t, we assume that it is a vegetable and we push it to the carry.veggies array. Then, critically, we return the object after we’ve modified it. That ensures that we save our updated running value.

Once we get to the end of the function, organizedFood should look like this:

{
    fruits: [ 'apple', 'banana' ],
    veggies: [ 'okra', 'spinach' ]
}

And here’s the exact same reducer in PHP:

$food = ['apple', 'okra', 'banana', 'spinach'];

$organizedFood = array_reduce( $food, function( $carry, $item ) {
    if( $item === 'apple' || $item === 'banana' ) {
        array_push( $carry['fruits'], $item );
    } else {
        array_push( $carry['veggies'], $item );
    }
    
    return $carry;
}, [ 'fruits' => [], 'veggies' => [] ] );

Again, the secret sauce here is the fact that we have this running value, $carry, that can be anything we want. And we have access to it on each iteration of our loop. On the last iteration, whatever we return as the $carry value will be what is returned by the array_reduce function.

Reducer redux

I hope this article has helped to demystify the reducer function and how it can be used for all sorts of things. In summary, a reducer function works like this:

  1. It iterates over list of things, running your callback function.
  2. It receives an initial running value, which can be anything.
  3. For each thing in the list, your callback has a chance to modify/replace the running value.
  4. After passing over the whole list, the reducer function returns the running value.

In JavaScript, you can call .reduce() directly on an array. In PHP, you use the array_reduce() function.

The next time you find yourself about to use a for loop for the purpose of looping over an array and keeping up with some other arbitrary value as you go, it’s almost certainly a great time to consider using a reducer.


  1. Since we’re using a JavaScript arrow function with no {}, then whatever our one-liner expression evaluates to is what is returned by the callback function.  ↩

]]>
https://blakewatson.com/journal/how-to-array-reduce-in-javascript-and-php/feed/ 0
Unpopular opinion: switching to WordPress in 2019 https://blakewatson.com/journal/switching-to-wordpress-in-2019/ https://blakewatson.com/journal/switching-to-wordpress-in-2019/#respond Mon, 10 Jun 2019 03:56:34 +0000 https://blakewatson.com/?p=221 Static sites continue to be popular with web developers, a trend that, as far as I can tell, began several years ago1. I joined that trend back in 2015 when I made blakewatson.com a static site.

I don’t have any ill-will toward WordPress. It’s a great CMS. It’s the CMS. But I just don’t need it for this site. I don’t need its commenting system, its URL handling, its media library, or its database. As they say, use the right tool for the job.

I went on to sing the praises of wok, a now defunct static site generator written in Python. While I still stand behind my comments about the benefits of static sites, I no longer agree with my quote above, and I have some better insight on the cons of static sites.

WordPress is really convenient

I don’t need its commenting system, its URL handling, its media library, or its database.

Maybe that’s technically true. I don’t need them. But it turns out that stuff is really useful2. Let’s say I notice a typo on my website while I’m on a device that doesn’t have my development set up. With WordPress, there’s no cloning, committing, and deploying just to change some text. I just log in, make the change, and save it. Easy peasy.

It’s true that the local dev environment of a static site is easier to get up and running. But it’s just me working on my site—the setup basically only happens once and I’m good to go. And I’m already used to WordPress and moving it around, thanks to working with it for over a decade. More often than not—for me anyway—the convenience of having that database outweighs the convenience of having my content versioned in a Git repo.

The performance of a static site rocks. I can’t deny that. My site gets very little traffic, so I’m hoping that doing some caching will get the job done.

But static site generators let you use Markdown.

Plot twist. I can still use Markdown with WordPress. In fact, I’m using it right now. I’m drafting this in iA Writer. When I get mostly finished, I’ll copy and paste the HTML into WordPress, fine tune it, and then publish. I realize this workflow isn’t for everybody, but that’s part of the beauty of WordPress—it can be fine tuned to an individual’s preferences. In my case, for example, I turned off Gutenberg as well as the classic visual editor, opting instead for the code editor only. I added a plug-in that gives me syntax highlighting. With most of my writing done in another app, editing a bit of HTML is no big deal.

WordPress is the Helvetica of CMSs

With 30% of the web, we might as well say that it is ubiquitous. And it’s good. Much like Helvetica, WordPress is very seldom the worst choice that you could have made (even if it isn’t always the best). It’s just not trendy to make your site with WordPress today. If you follow web circles, you will hear a lot about Gatsby, Next, Nuxt, and a whole bunch of other stuff. That’s totally fine, it’s just important to remember that new tech gets chatter:

You don’t hear about TextMate because TextMate is old. What would I tweet? Still using TextMate. Still good.

— David DeSandro, Metafizzy blog

While there are great places to keep up with WordPress, in most publications on the web, you are much more likely to read about bleeding edge tech .

Simple and boring

I admit that the static site generator I chose was a bit obscure, but still, there’s not a static site generator with the popularity and longevity of WordPress. It’s being actively developed, and just about any problem you could come across has already been documented and answered. I got burned by wok (heh). I got burned by picking something trendy instead of something established.

At the risk of coming off as a curmudgeonly web developer, I’m becoming kind of obsessed with the idea keeping things simple and boring. At least when it comes to code. I’m still very much in favor of weird homepages, though, and I think everybody should have one.


  1. I mean, yeah. I guess static sites were the first kind of websites. They’ve always been a thing. But when I say they are trending, I’m referring to the uptick in web developers using modern static site generators.  ↩

  2. Okay maybe not commenting system. And not because it’s not a cool technical feature. But just because comment sections on the Internet kind of suck.  ↩

]]>
https://blakewatson.com/journal/switching-to-wordpress-in-2019/feed/ 0
Playing Minecraft by voice with Talon https://blakewatson.com/journal/playing-minecraft-by-voice-with-talon/ https://blakewatson.com/journal/playing-minecraft-by-voice-with-talon/#respond Tue, 12 Feb 2019 23:35:14 +0000 http://blakewatson.com/?p=132 In times past I was an avid console gamer. Those days ended about a decade ago when my hands became too weak to operate a controller. I began to be interested in PC games that had simple controls and mechanics. At one point, with the help of my iPhone, I was able to access enough keys to play the beloved Minecraft. Several years ago I lost the ability to play that game as well. However, I’m back to playing Minecraft again—this time by voice with Talon.

Full keyboard access

What makes playing Minecraft possible is having full access to the keyboard. I’m able to control a mouse. And I can press the left click button (sometimes a second button, depending on my setup). After turning on auto-jump, a built-in Minecraft feature, I depend on Talon to handle all other functionality. Even without customization for Minecraft, including the basic_keys.py example script is enough to give you access to your inventory as well as the ability to place blocks.

There are two important functions that require more nuanced behavior than simple keypresses—walking and digging. When I’m using my two-button setup, I can use one button for walking forward and the other for digging. But when I’m using my one-button setup, I need to use Talon for one of those. That’s where customization comes in.

Custom Talon scripts for Minecraft

I explained in my last article about Talon how Talon can be scripted to perform custom actions. Scripting Minecraft controls required me to dig in a little deeper, going beyond keypresses by programming macro-like sequences.

For example, digging. I needed the ability to hold down the attack key for specific amount of time, depending on the block I’m trying to break. I came up with this format:

"attack AMOUNT_OF_TIME"

I settled on using half-seconds as the time increment. With the command added to the keymap, it looks like this:

ctx.keymap({
    'attack (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)': hold_key
})

To use this command, I speak the word “attack” followed by a number one through nine. When Talon recognizes that command, it passes my dictation to the hold_key function. The hold_key function looks like this:

from talon import ctrl
from ..utils import parse_words_as_integer

def hold_key(m):
    keymap = {
        'attack': 'k',
        'eat': 'u'
    }
    half_seconds = parse_words_as_integer(m._words[1:])
    if half_seconds != None and half_seconds > 0 and half_seconds < 10:
        microseconds = half_seconds * 500000
        key_to_press = keymap[m._words[0]]
        ctrl.key_press(key_to_press, hold=microseconds)

There’s kind of a lot going on here, but essentially what’s happening is that it’s figuring out which key I want to press and then it’s holding it for the specified number of half-seconds. The parse_words_as_integer function comes from the community utils.py script. It’s taking a spelled out number from my dictation (e.g., “four”) and turning it into an actual number (e.g., “4”)—an integer type—that I can use to set the hold time.

It’s a handy command, but when I’m doing a lot of digging, it gets repetitive. That’s okay, because I have another option. I made a second command for attack that simply holds down the key until I tell it to stop.

def start_attack(m):
    ctrl.key_press('k', down=True)

def stop_attack(m):
    ctrl.key_press('k', up=True)

Added to the keymap, it looks like this:

ctx.keymap({
    'attack (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)': hold_key,
    'hitter': start_attack,
    'stop': stop_attack,
})

If you’re interested in more, you can see my full Minecraft script as well as the rest of my user scripts on GitHub.

Endless possibilities

After being limited to mouse-only and turn-based games for a while now, I’m excited by the possibilities Talon brings for gaming. I can imagine writing custom scripts for something like World of Warcraft—speak the phrase “go ham,” and my character starts firing off its attack rotation. Talon could also help with in-game text chat, something that hasn’t been feasible for me before.

I honestly can’t say whether I’m having more fun writing scripts for Talon or using them to write, code, and play games. #nerd #winning

]]>
https://blakewatson.com/journal/playing-minecraft-by-voice-with-talon/feed/ 0
Writing and coding by voice with Talon https://blakewatson.com/journal/writing-and-coding-by-voice-with-talon/ https://blakewatson.com/journal/writing-and-coding-by-voice-with-talon/#respond Mon, 31 Dec 2018 23:32:39 +0000 http://blakewatson.com/?p=127 I previously wrote about writing and coding with the macOS Accessibility Keyboard. It continues to be the most essential piece of assistive technology I use. That said, I recently discovered another amazing tool, Talon.

In recent months, I’ve been spending more time out of my wheelchair in order to get some pressure relief for my legs. As such, I’ve been working a lot on my laptop. I use it in a lying down position, which makes using the mouse somewhat difficult for me. My biggest hindrance is typing on the Accessibility Keyboard.

Enter Talon. Talon is freeware software for macOS that provides “powerful hands-free input” by way of eye-tracking and voice recognition. In particular, its voice recognition system is novel, offering a user experience that’s quite different (and, to me, better) than what you get with Dragon or macOS’ built-in solutions1.

The trick is that Talon doesn’t simply type out whatever you dictate. It listens to you then determines what to type based on a set of rules that you can control. For example, if I say:

title hello world

Talon will output:

Hello World

This is because I have a script installed that knows if it hears a phrase beginning with the word title, it should output the rest of that phrase with each word being capitalized.

Whereas if I had said:

hello world

Talon would have output nothing at all. Why is that? Because I don’t have any rules installed that care about the phrase, hello world. If I did want to literally type hello world, I would dictate:

say hello world

By starting the phrase with say, Talon knows—again, because of a currently installed rule—to output the rest of the phrase normally.

Several of these formatters are included in this particular ruleset. Here are just a few of them:

  • sentence hello worldHello world
  • camel hello worldhelloWorld
  • snake hello worldhello_world
  • spinal hello worldhello-world
  • smash hello worldhelloworld

Why Talon? Isn’t Dragon or macOS dictation way better?

Well, that depends. Coders will immediately recognize from my above example what kind of flexibility Talon provides in controlling your output. If you are simply writing natural language, you might not find Talon as useful. But if you need precision—as with programming and editing—Talon really shines.

Its number one feature, in my opinion, is its ability to be customized.

Controlling Talon with scripts

For many purposes, the community scripts are enough to provide great functionality. But the real fun is in customizing it to do exactly what you want. Let’s take look at how that works. To do that, we need to use Python.

When you install Talon, it creates a user folder where you can add scripts to control how Talon behaves. You’ll find it at:

~/.talon/user

This is where you can drop in some Python scripts. Any scripts you put there will be loaded automatically by Talon.

Community scripts

Let’s first take a look at existing scripts you can use to get Talon up and running with some basic functionality. You’ll find some great starting scripts in the talon_community repo.

A good start is to include these three files:

  • utils.py: used by a bunch of the other files
  • misc/basic_keys.py: what it says on tin, plus a spelling alphabet
  • misc/std.py: includes the formatters I described in the intro

Just drop utils.py into the user folder. Then create a misc folder in user and drop the other two files in. Feel free to edit these files. Personally, I removed a good bit of the coding-related commands from std.py because they either didn’t support my primary languages or were registering as false positives.

Once you launch Talon, it will load those files and you will have access to their functions. For example, basic_keys.py includes a spelling alphabet for typing single letters—air becomes a, bat becomes b, and so on. This method gives you greater precision than normal dictation gives you and is especially useful for coding, which contains many terms that can’t be dictated easily2.

Writing custom scripts

Of course the real fun is writing your own scripts3. Let’s say, like me, you enjoy using the em dash () character. You want to add it your repertoire in Talon. Start by creating a new file, my_scripts.py. We’re going to be using a module from Talon that is needed to create a keymap—the thing that maps our commands to the output we want.

from talon.voice import Context

Let’s say we want to type by dictating bigger dash. We would construct our keymap like so:

ctx = Context('my_scripts')

ctx.keymap({
    'bigger dash': '—'
})

That’s all there is to it. Congratulations, you just wrote your first Talon script!

But let’s keep going. We’ll add a command for activating Spotlight in macOS (command + space). In order to press the modifier key, we need to import something else from Talon. After our addition, the code should look something like this:

from talon.voice import Context, Key

ctx = Context('my_scripts')

ctx.keymap({
    'bigger dash': '—',
    'spotlight': Key('cmd-space')
})

Now when we say spotlight, Talon will virtually type the shortcut for summoning Spotlight search.

Scratching the surface

That’s all we’ve done. Snoop around in the talon_community repo and start to see how powerful Talon really is. If you’re not a coder, don’t worry, there’s a friendly Slack where you can get help from the developer of Talon4 as well as the community. If you have trouble typing, but haven’t had any luck with dictation in the past, give Talon a try.


  1. Talon actually makes use of Dragon if it’s installed, but also comes with built-in voice recognition, which is what I use.  ↩

  2. If Talon isn’t picking up certain words or some words are getting false positives, don’t be afraid to experiment with different words. Even if you can’t code, changing the words is as simple as finding the the word list in basic_keys.py and editing it.  ↩

  3. Note that, at the time of this writing, there is a new API on the horizon. So keep an eye on the release notes and community repo for changes.  ↩

  4. The developer has a Patreon so, if you find Talon useful, consider supporting its development.  ↩

]]>
https://blakewatson.com/journal/writing-and-coding-by-voice-with-talon/feed/ 0
Writing and coding with the macOS Accessibility Keyboard https://blakewatson.com/journal/writing-and-coding-with-the-macos-accessibility-keyboard/ https://blakewatson.com/journal/writing-and-coding-with-the-macos-accessibility-keyboard/#respond Sat, 02 Jun 2018 23:31:17 +0000 http://blakewatson.com/?p=125 For years, macOS lacked a proper on-screen keyboard. The built-in Keyboard Viewer provided little relief. Many disabled macOS users turned to a third-party option, a $300 piece of software called AssistiveWare KeyStrokes. Despite its cost, it served me well for years. But the app was old and development had essentially ceased. Fortunately, in September 2017, Apple shipped a utility in macOS High Sierra called Accessibility Keyboard.

Side by side view of AssistiveWare KeyStrokes and Accessibility Keyboard

On-screen keyboard basics

For many users who are unable to type on a physical keyboard, on-screen keyboards (OSKs) are a necessity. As with any app, the nuanced details matter. At a bare minimum, though, an OSK must have the following features to be useable:

  • A QUERTY layout with access to all keys
  • Sticky modifier keys that stay held on click

But for proper, productive use, an OSK needs more:

  • Customizable key layout
  • Word prediction
  • Custom keys for text macros
  • Resizable window
  • Configurable window transparency

The macOS Accessibility Keyboard has all this and more.

Accessibility Keyboard customization

The most important feature to me is the ability to customize the keyboard layout. I’m a diehard fan of the Chubon layout, developed by Dr. Robert Chubon years after he was paralyzed in a childhood accident. It places common keys in the center of the keyboard, offering a target-based approach with the letter “E” in the center.

Accessibility Keyboard ships with a panel editor that allows you to design complex, powerful keyboards. Keys can simulate keystrokes, enter text strings, and even run AppleScripts. You can set text labels on keys or use images instead. And you aren’t limited to one layout. Accessibility Keyboard holds multiple panels and provides a mechanism for switching between them.

Overall, it does a fantastic job at replacing my beloved KeyStrokes. It has a few glitches here and there, and seems to need a good amount of RAM, but it’s good quality for being a version 1—and Apple is actively developing it.

Using Accessibility Keyboard for web development

My customized version of Accessibility Keyboard with custom functionality keys labeled

I use a heavily customized version of Accessibility Keyboard that’s optimized for writing code. It includes single-click access (no Shift required) to all the punctuation and special characters required in programming syntaxes. Moving around in text can be tedious with a mouse or arrow keys alone. To help with that, I added keys for moving the text cursor by word, jumping to the beginning and end of lines, and selecting text by character or word.

To aid in coding, I added some common strings (I also take advantage of my code editor’s snippets feature) as well as shortcuts for indenting and commenting code. Since I’m a web developer, I do a lot of browser refreshing1. The Last key helps me quickly switch between my editor and browser2, where I can then use the reload or hard reload keys.

As general productivity helpers, I have keys for summoning Alfred, my app launcher of choice, and invoking macOS utilities like Mission Control and the screenshot tool.

Get my layout

You can download my layout here. You will also need to download the accompanying AppleScript as it doesn’t seem to carry over properly. In fact, you will need to open the panel editor and re-assign the AppleScript to the Last key.

Email me if you have any questions or comments about this layout. And if you have one of your own, please share!


  1. Yes, I know there are apps and npm packages that can reload the browser for you, but I’m a control freak, okay?  ↩

  2. Because of some glitchy behavior with Accessibility Keyboard and the app switching shortcut, I had to have the key trigger an AppleScript to get this to work.  ↩

]]>
https://blakewatson.com/journal/writing-and-coding-with-the-macos-accessibility-keyboard/feed/ 0
My answer to the gratuitous new tab page https://blakewatson.com/journal/my-answer-to-the-gratuitous-new-tab-page/ https://blakewatson.com/journal/my-answer-to-the-gratuitous-new-tab-page/#respond Sun, 22 Oct 2017 23:07:37 +0000 http://blakewatson.com/?p=123 Several years ago, Safari shipped with a terrible, over-the-top new tab page that showed your recently visited sites as a giant TV grid. It even had the signature reflective floor. It looked like this:

Safari 4 recent sites
Safari 4’s new tab page is an inwardly-curved, three dimensional grid of website thumbnail images

The situation has improved somewhat since then. Current versions of Safari and Chrome still show a grid of recent site screenshots (though Safari has options to show something else1), but they embrace a softer, more elegant design.

Still, I find having a handful of website screenshots on a fresh tab to be incredibly inefficient and, frankly, ugly2.

So several years ago I created an HTML file with a pretty list of links and used that as my new tab page. When I tired of editing HTML just to add a link to the page, I made it into a single page application that saved the links to the browser’s local storage. That became Start, a downloadable file or hosted page that I offered for free. I used it happily for several years, but struggled to keep my links synced between browsers.

Introducing A Fine Start

Fast forward to 2017. I began rewriting Start from scratch with the goal of adding user accounts for syncing links. I released A Fine Start to some testers in June and now it’s publicly available as a hosted web page or Chrome extension. The idea is simple—you open a new tab, you get a list of links, grouped and sorted to your liking.

A MacBook showing the A Fine Start page—three columns, each made up of lists of links to various websites

A Fine Start is for getting in, then getting out. It doesn’t force your brain to parse tiny images of websites or stress over which sites to “pin” in place. Add whatever groups and links you want. They’re stored in local storage, and you can import/export them to share them between browsers and devices. No emails, accounts, or payments necessary.

For $5 a month, you can get a Premium account that will automatically keep your links synced.

The zen of the list

I’ve been using this system in one form or another for years now and whenever I’m using a web browser without it, I feel lost. I suspect the keyboard-oriented folks couldn’t care less about what shows up in a new tab—they’re going to immediately invoke the location bar’s search and suggest functionality. But for the click-happy (or OCD organizers) among us, seeing a neatly displayed list of links is supremely satisfying. No messy screenshots and no BS.

Designers, you may appreciate the minimal aesthetic with an obsession over typography. This is far from a plain old ugly list of links. I went through several iterations of list, link, heading styles to get the right look. The design embraces cool gray for negative space which is intended to make it feel more like a native tool than a web page.

Try it out

A Fine Start is absolutely free for the majority of its functionality. Give it a shot and let me know what you think. If you love it and want to use it everywhere, consider grabbing an account. I think you’ll enjoy it as much as I do.


  1. Including a custom URL, which is how you may use A Fine Start in Safari.  ↩

  2. Have a list of news sites? Now you have notoriously bad web design plastered all over every new tab.  ↩

]]>
https://blakewatson.com/journal/my-answer-to-the-gratuitous-new-tab-page/feed/ 0
Why I left Facebook https://blakewatson.com/journal/why-i-left-facebook/ https://blakewatson.com/journal/why-i-left-facebook/#respond Tue, 04 Jul 2017 23:02:56 +0000 http://blakewatson.com/?p=119 I joined Facebook in April 2005 shortly after it was opened up to Mississippi State students. Despite being an early Facebook supporter, I deleted my account back in May 2017. Privacy and ethical concerns rank at the top of the list, while other factors—my waning usage of the service for example—also contribute.

The price of Facebook is privacy

Companies that do business in big data have astonishing access to information about you, much of which you provide to them yourself and the rest they collect by any number of creepy but (presumably) legal ways. In the case of Facebook—which recently hit the 2 billion user mark—we’re talking data mining on a truly massive scale. Facebook is building complex profiles on you, gathering thousands of data points, and even purchasing information it doesn’t already know about you from data brokers. Even if you aren’t a Facebook user, they likely still keep a record of you in the form of shadow profiles.

You’ve seen those Facebook like buttons and “Sign-in with Facebook” buttons on other websites? Yeah, that’s Facebook tracking you around the internet. Their privacy policy says clearly:

We collect information when you visit or use third-party websites and apps that use our Services (like when they offer our Like button or Facebook Log In or use our measurement and advertising services). This includes information about the websites and apps you visit, your use of our Services on those websites and apps, as well as information the developer or publisher of the app or website provides to you or us.

If you were tasked to write down everything you think Facebook knows about you, you probably would get a lot of it right by just thinking through it. Like, of course they have all the information I give them. And with a bit more reasoning you might realize of course they track me when I use their widgets on other websites. But they also collect information that you would likely not consider.

For example, Facebook tracks posts and comments even before you post them. That time you were about to tell somebody off for their dumb opinion but then, in a sudden rapture of empathy, decided not to hit “Send”—Facebook collects that.

John Gruber of Daring Fireball writes:

Sending form data surreptitiously is morally wrong, and everyone knows it.

This might sound hyperbolic, but I mean it: I think we’d be better off if JavaScript had never been added to web browsers.

As a person who writes a lot of JavaScript and (mostly) enjoys it, this makes me sad because while I enjoy the interactivity it provides, I must concede that big data companies and advertisers have weaponized it against us.

In addition to the information Facebook collects about you, there are the entities that your data gets shared with—all those silly games and quizzes people are adding to their account are making off with all sorts of information about you.

At this point, you may be thinking, So what? I know they’re tracking me. So is Google and every other website. Who cares?

It’s a valid question. All of this tracking might not personally, directly affect you in a bad way now—aside from a lot of creepy advertisements—but unfortunately, the trend is getting worse. And there are negative consequences to the pillaging of your privacy by Big Data:

Tech entrepreneur and educator, Salim Virani, writes:

If you’ve ever admitted to something illegal in a private Facebook message, or even mentioned your support for a political cause, this can be used against you in the future, especially by another country’s government. You may find yourself arrested for being at the wrong place at the wrong time, or just pulled aside at the airport one day, now facing jail time because you revealed you did something that government considers illegal 5 years ago. One New York comedian had a SWAT team break into his house based on a joke post. Law enforcement often acts in error, and you’re giving them more power and more chance of error. You’re loading the gun, pointing it at your head, and handing it to every trigger-happy “enforcer” who’s willing to buy your data.

If your privacy and that of your loved ones is at all important to you, now is the time to start taking steps to maintain it.

Emotional manipulation and the slot machine effect

Over time, we’ve become hooked on the social validation Facebook (and other services) provide. Before I hit the delete button on my account, one of the last things that kept me on Facebook—after I had largely stopped posting and reading the News Feed—was simply checking my notifications. I unconsciously craved that little hit of happiness one gets when they see, So-and-so liked your post. But that’s not real happiness. It’s an unhealthy addiction.

Julian Morgans of Vice writes:

Former Google designer and ethicist Tristan Harris lays out the most common ways we’re being manipulated on his blog. And as he explains, all of them use something called intermittent variable rewards.

The easiest way to understand this term is by imagining a slot machine. You pull the lever to win a prize, which is an intermittent action linked to a variable reward. Variable meaning you might win, or you might not. In the same way, you refresh your Facebook updates to see if you’ve won. Or you swipe right on Tinder to see if you’ve won.

Not only can Facebook use its power to manipulate our emotions, it experiments on its users without their knowledge and consent. In 2012, Facebook worked with researchers to carry out a large-scale study that manipulated the emotions of its subjects. From the paper’s abstract:

In an experiment with people who use Facebook, we test whether emotional contagion occurs outside of in-person interaction between individuals by reducing the amount of emotional content in the News Feed. When positive expressions were reduced, people produced fewer positive posts and more negative posts; when negative expressions were reduced, the opposite pattern occurred.

When you are reading your News Feed, you might reasonably assume that it consists of posts from your network of friends, roughly ordered by date. In reality, it’s a sick concoction of friends’ posts and advertisements, strategically and algorithmically generated to manipulate you for the purpose of keeping you interacting with Facebook’s real customers—its advertisers.

Even if you know about all this stuff—or at least have a vague idea—you’ll likely find it difficult to do anything about it. Because of network effects, Facebook has something of an emotional lock-in on us. The very thought of leaving Facebook can induce a serious case of FOMO in even the most anti-social INTJ.

Ripping off the band-aid

Here’s the thing. You won’t be as outcast and lonely as you think. Your friends—and I know this is hard to believe—are still your friends in real life. You just won’t get notifications of what they ate on their lunch break. Unless, you know, you follow them in the myriad other ways it’s possible to do so online. Look, I’m not saying we have a mass exodus from the interwebs. I’m talking about one website.

Not that they make it easy. Don’t be fooled into merely “deactivating” your account—something that people often do because they need a break from Facebook1. Deactivation means you artificially disappear from Facebook until you get ready to come back. Then it’s all waiting for you right where you left off. To delete your account fully, you will need to jump through the following hoops.

  1. Dig into the support section to find this page that tells you how to delete your account.
  2. Optionally, you can download your data from Facebook. I didn’t find this to be very useful and I’m pretty sure there’s some stuff missing. But that said, it helps ease the fear of loss that accompanies this process.
  3. Finally—and this is the hardest part—you must pass Facebook’s test of your fortitude by not logging into your account during the 14-day grace period leading up to the deletion of your account.

It’s difficult to know how much of your information will be truly deleted. You have to go into this process with the assumption that it’s none. I personally hold on to a hope that some of the basic stuff is deleted (e.g., my posts) but I realize that’s naive. Think of it more in terms of not providing them any more information than what they already have.

Replacing Facebook

This is going to look differently for everyone, depending on their preferences and their friends’ preferences. For me that could mean spending more time on Twitter. Twitter is in many ways in the same boat as Facebook as far as advertising goes, but at least you know from the outset that everything is public2.

I’m hoping that it will mean more time making content like this for my website—yes, it turns out there’s already a built-in way to have your own “profile” where you can post things and people can “follow” you, and it’s called Your Own Website™—but I know better than to promise content.

If you are interested in short form posting, check out the recently Kickstarted Micro.blog platform. Not sure if I will be using it much but I like the idea.

Of course, I would be remiss not to mention that you can simply call your friends and talk to them—your smartphone comes with a built-in app that will let you do just that. You can even get together in the same location and hang out (novel, I know!).

The extra mile

This article is all about Facebook, but I have also scaled back my use of other big services like Google. For search, I recommend DuckDuckGo, a search engine that doesn’t track you. Instead of using a free email provider, I use Pobox, an independent email provider, for $50 a year.

A lot of folks use ad blockers—which I highly recommend—and I also use a browser extension called Ghostery that blocks all the various trackers you find all over the web3.

Lastly, if you are interested in reading more, here are a few articles that I found invaluable while putting this article together.

Get your loved ones off Facebook.

What should you think about when using Facebook?


  1. The fact that this feature exists tells you it’s a potentially harmful thing to have in your life.  ↩

  2. Yes, I’m ignoring the fact that some people prefer private Twitter accounts.  ↩

  3. It blocks a lot of third-party JavaScript that you might want to allow. Personally, I start off by blocking everything but then I allow services like Gravatar that I don’t mind running. It’s worth mentioning that Ghostery has an opt-in setting for gathering data about the trackers you encounter (tracking the trackers). Personally, I recommend not opting in as it’s unclear exactly what’s being tracked and how that data’s being used.  ↩

]]>
https://blakewatson.com/journal/why-i-left-facebook/feed/ 0
An ode to web pages https://blakewatson.com/journal/an-ode-to-web-pages/ https://blakewatson.com/journal/an-ode-to-web-pages/#respond Sat, 04 Mar 2017 23:01:30 +0000 http://blakewatson.com/?p=117 Last year, when I read this satire of modern web development, I chuckled. When the npm left-pad dependency controversy left many npm projects unable to build, I questioned what we were doing. These days, I just chalk it up to the maturing of web development. It’s the JavaScript renaissance, right?

But in my heart of hearts, I miss the early days, when I was nothing more than a website enthusiast. Back then, there was nothing to npm install. No dependency trees. Version control meant adding the suffix “-old” to file names. I thought my code-editor with built-in FTP to be the ultimate tool because you could edit files directly on the server! Local development was as simple as editing a doc in a folder. Did you know that web browsers are perfectly happy to open an HTML file on your computer? No domain, hosting, MAMP, Vagrant, or database necessary.

All of our profiles look alike

Before social media, before apps, before vlogs, blogs, and the like, there were web pages. Often, someone would refer to their online profile as their “home page.” I kind of feel bad for web developers just starting out who may not experience the wonderful delight of designing and building their own unique and quirky online abodes. Don’t get me wrong, many developers have personal websites. But they are just boring blogs and portfolio sites1 like the one you are reading now. They’re well designed, minimal, or look like Bootstrap. But they used to be alive with personality.

Before social media monoliths made us into little mechanical turks for advertising platforms, we had organic homes on the web. We had pages that were ours. And they could look however you wanted. And you could write whatever you wanted on there. And there wasn’t a “report/flag” button. There weren’t weirdos tagging themselves in your photos. There weren’t advertisements if you didn’t want them. There weren’t comments if you didn’t want them. There were no photo dimensions to adhere to. No 140-character limits. No BS. Or lots of BS. Either way, the choice was yours because you owned your site and you could do whatever you wanted.

How to make a web page

Spoiler alert. This is still a perfectly valid way to make a website. Just because you see a bunch of tutorials about hard-to-use frameworks, libraries, and services doesn’t mean you have to use them. Just because you can use build scripts, CDNs, Sass, Less, React, Gulp, Grunt, WordPress, Laravel, Django, Rails, or whatever it is, doesn’t mean you must.

If you are just starting out, here’s an excellent guide by Chris Coyier on CSS-Tricks that will get you through the technical hoops of getting a domain and a web server. From the there you can start making a web page.

Don’t know HTML or CSS? Try this beginner guide on HTML Dog.

Challenge

Make a web page. Make it about anything you want. Put it on your web server or hosting service. Don’t have web hosting? You can some at NearlyFreeSpeech.NET for $0.252. Then share it with the world.

Addendum

Chris Hawkes, a developer I follow on YouTube, happened to post a video that coincides with my point here that web development has gotten kind of crazy.


  1. I’m exaggerating to make a point. I don’t want a resurgence of sparkling MySpace profiles any more than you do.  ↩

  2. NearlyFreeSpeech.NET doesn’t have an affiliate program—I get nothing by linking to them. I do it because they are awesome.  ↩

]]>
https://blakewatson.com/journal/an-ode-to-web-pages/feed/ 0
2016: My year in review https://blakewatson.com/journal/2016-my-year-in-review/ https://blakewatson.com/journal/2016-my-year-in-review/#respond Sat, 31 Dec 2016 22:55:09 +0000 http://blakewatson.com/?p=115 With 2016 coming to a close I wanted to take a moment of reflection — a temporary reprieve from thinking about the future — to review some of my accomplishments on the year. This year had quite a few firsts. I joined the Apple Developer Program and published my first-ever app to the Mac App Store. Working with the Mad Genius team, I had the pleasure of being part of some cool website launches this year. And in the area of professional growth, I picked up some new skills I have been wanting to tackle for a while now.

But first, some non-tech stuff

This not-so-humble article concerns itself largely with the web-related stuff I’m into, but other notable events in my larger circle deserve a mention. Among them:

  • A new treatment for spinal muscular atrophy became the first one to get FDA approval.
  • My brother, a former Spanish lecturer and PhD student, switched careers abruptly to join Mad Genius as a web developer.
  • The weirdest U.S. presidential election I’ve ever seen happened.

Critical hit

This year, I started a Dungeons & Dragons group with some friends online. Being an app junkie, I searched many an hour for apps to make my life as the dungeon master easier. Running a D&D game takes a lot of dice rolling as it turns out, and none of the dice rolling apps I tried worked like I wanted. Being the designer/developer I am, I decided to make my own. I’ve always wanted to make an app and sell it. Thanks to an awesome tool called Electron, I was able to take the web coding skills I already had and repurpose them into making a Mac application. The result is d20, a design-y RPG dice roller.

D20 main

d20 – an elegant, powerful dice roller

It’s not a grand technical achievement, but it is the culmination of a lot of nights and weekends. Let’s be honest, I made this for me and didn’t expect to win any awards or make a ton of money. But I am thrilled with the sixty-something dollars of sales I’ve made since releasing the app in 2016. It was also a fun challenge to execute all the branding surrounding a publicly released app — from the app icon and App Store description to the marketing and support website.

I won’t go into a lot of detail here, but if you are interested, check out d20 on the App Store (or kick the tires with the free web-based version).

Everything old is new again

Blakewatson com 2016
The newly designed homepage of blakewatson.com

Over a year ago, I wrote about how my website’s design was not original. The situation has improved, though, because in October, I redesigned my site. According to my estimates, this constitutes the thirteenth iteration of blakewatson.com since I first bought the domain in 2005.

After fellow Genius Shawn Palmer designed a new (awesome) mark for me last year, I knew I needed a website that would do the new mark justice. With limited available time, this was the best I could do. 🙂

A banner year in the labs

2016 was my first full calendar year as an Interactive Designer at Mad Genius. We launched a lot a of great websites for our clients this year and I am incredibly proud an honored to have been a part of that. Among the sites I had a hand in were:

  • Mississippi Organ Recovery Agency’s “It’s Super Easy” campaign
  • Mid-South Medical Imaging
  • Reformed Theological Seminary’s 50th anniversary microsite
  • Laguna Blue, a new microfiber towel manufacturer
  • Mississippi Center for Plastic Surgery – a website Matt and I got to build together

Last but not least, I had the honor of leading the development side of the new Mad Genius website.

All in all, it’s been a fun year in the labs and I can’t wait to see what 2017 has in store for us.

Lessons learned

In addition to making a lot of stuff this year, I learned a few things. A bunch of projects I’ve been working on lately have given me the opportunity to experiment with web animation. That’s been fun to play around with and it’s a useful tool in the interactive designer’s tool belt.

I’ve also been pushing forward with my command-line and server administration skills. Without getting too into the weeds, let’s just say I now know how to set up a basic web server on a Linux computer and put it on the internet!

Happy New Year

I hope you will take a moment to think about your own accomplishments in 2016. If you have ever thought about getting into programming or web development, there’s no time like the present. Why don’t you go ahead and make it a New Year’s resolution? If you need help, email me and I will try to point you in the right direction.

But no matter what you are into, whatever your passion may be, keep it up.

Here’s to a new year.

]]>
https://blakewatson.com/journal/2016-my-year-in-review/feed/ 0
SVG logo animation with Anime.js https://blakewatson.com/journal/svg-logo-animation-with-anime-js/ https://blakewatson.com/journal/svg-logo-animation-with-anime-js/#respond Fri, 14 Oct 2016 22:05:03 +0000 http://blakewatson.com/?p=110 Many a library exist for animation on the web these days. Anime is my favorite. It’s approachable 1, comes with decent documentation and examples, and packs phenomenal cosmic power in an itty bitty living space (9.15 KB minified).

Anime lets you run animations on just about anything — CSS, SVG, DOM attributes — but what I love most about it is its ability to run animations on individual CSS transform properties. With Anime, you can write something like this:

anime({
	// specfiy a string selector, a DOM node, or an array of those things
	targets: ".a-dead-body",
	// we can animate on a specific transform property
	translateX: 100,
	// as many of these as you want
	rotate: 180,
	// doesn't have to be a transform, anything numerically-based will do
	color: "rgb(255,0,0)",
	// how long?
	duration: 1000,
	// cool built-in easing curves
	easing: "easeOutElastic",
	// do something afterward
	complete: function() {
		console.log("It's alive!");
	}
});

Which could produce something like this:

💀 Click me!

See?

Of course, that’s a nearly useless animation. Let me show you a real-world example — the logo animation on the home page of this very website.

My logo (or personal mark, as I like to call it) is an SVG that resembles the initials “bw” stylized to look like a futuristic “w”. Take a look below and, when you are ready, click the animate button to see how we want our final animation to look.

In order to achieve this effect, we need to break the logo into three distinct pieces:

  • #ex1-logo
    • #ex1-logo-left
    • #ex1-logo-middle
    • #ex1-logo-right

First we need to get our logo into its pre-animation state — the starting point. To do that, we need to do the following:

  1. Make sure the whole logo starts off transparent
  2. Explicitly make the middle piece transparent
  3. Bump the entire logo down by half of its height
  4. Swap the left and right pieces

Update 10–23–2017: Safari 11 requires that transform-box: fill-box; be set on each piece in order to translate each piece by a percentage of its own height or width.

$("#ex1-logo").css({
	"transform": "translateY(50%)",
	"opacity": 0
});
$("#ex1-logo-middle").css("opacity", 0);
$("#ex1-logo-left").css("transform", "translateX(100%)");
$("#ex1-logo-right").css("transform", "translateX(-100%)");

Good. If you could see it at this point, it would resemble an arrow pointing upward. Now we can bring in Anime.

First, we want to move the whole logo group back up to its original position while simultaneously fading it in.

anime({
	targets: "#ex1-logo",
	translateY: ["50%", "0%"], // start value, end value
	opacity: 1,
	easing: "easeOutSine",
}); 

Sweet. As soon as that animation finishes, we want to do two things simultaneously:

  • Move the left and right pieces back to their original positions
  • Fade in the middle piece

We can latch on to Anime’s complete callback in order to run code after the first animation finishes.

anime({
	targets: "#ex1-logo",
	translateY: ["50%", "0%"], // start value, end value
	opacity: 1,
	easing: "easeOutSine",
	complete: function() {
		// MORE ANIME HERE!
	}
}); 

I’m going to fire two different Anime animations inside that callback. First, the hard one — swapping the left and right pieces back to their original position.

anime({
	targets: "#ex1-logo",
	translateY: ["50%", "0%"], // start value, end value
	opacity: 1,
	easing: "easeOutSine",
	complete: function() {
		anime({
			targets: ["#ex1-logo-left", "#ex1-logo-right"],
			translateX: function(el, index) {
				if($(el).attr("id") === "ex1-logo-left") {
					return ["100%", "0%"];
				} else {
					return ["-100%", "0%"];
				}
			},
			easing: "easeInOutQuad",
			duration: 500
		});
	}
}); 

What?! We have two targets? And what is this translateX voodoo?

A very cool feature of Anime is its ability to accept a function instead of a static value. The function will run for each target specified, and automatically recieves the current DOM element (el) and the zero-based index of the current target. Very similar to a jQuery .each() function, if you are familiar with that.

For each target, we can now do some logic to return back the value we want. In our case, we want to give the left piece different start and end points from the right piece. That’s done with an if statement that checks the element’s id.

On to the next part. We’re just going to run another Anime animation at (roughly) the same time by calling it right after this one.

This is the final state of our animation code, so here it is all together now:

$("#ex1-logo").css({
	"transform": "translateY(50%)",
	"opacity": 0
});
$("#ex1-logo-middle").css("opacity", 0);
$("#ex1-logo-left").css("transform", "translateX(100%)");
$("#ex1-logo-right").css("transform", "translateX(-100%)");

anime({
	targets: "#ex1-logo",
	translateY: ["50%", "0%"], // start value, end value
	opacity: 1,
	easing: "easeOutSine",
	complete: function() {
		anime({
			targets: ["#ex1-logo-left", "#ex1-logo-right"],
			translateX: function(el, index) {
				if($(el).attr("id") === "ex1-logo-left") {
					return ["100%", "0%"];
				} else {
					return ["-100%", "0%"];
				}
			},
			easing: "easeInOutQuad",
			duration: 500
		});

		anime({
			targets: "#ex1-logo-middle",
			opacity: 1,
			easing: "easeInOutExpo",
		});
	}
}); 

Lastly, we fade in the middle piece, and we even use a fancy smancy easing curve to do it.

One more time, with feeling

The final animation looks something like this, as we saw earlier.

If that seemed like a lot, don’t worry — it kind of is. It might seem a bit odd at first, but it’s just a matter of thinking through what you want to do. Think about it in terms of a starting point and an ending point. Then write out in words what you want to accomplish. If you get stuck, the docs are your friend.

Want to see more tutorials? Get in touch and let me know what you would like to see.


  1. Learning any library is hard. But Anime’s properties correspond closely with CSS properties which helps I think.  ↩

]]>
https://blakewatson.com/journal/svg-logo-animation-with-anime-js/feed/ 0