Astute Readers, Lazy Loaders

Astute readers of this blog may notice that I have been employing certain CSS properties on this site as I write about them. Today I added lazy loading for images using the Intersection Observer API, and I will tell you about it.

Since this site uses infinite scroll for posts, I decided to load the images as they come into view rather than all at once. Here is a great overview of the Intersection Observer API with some examples. In brief, it observes if a target element intersects an ancestor—in this case when an image enters the viewport—and performs a callback. Because the API is still experimental, not all browsers support it and you will have to write a fallback behavior.

First, in HTML images are given the class lazy-image, and instead of src attribute, they use data-src to define the sources to be loaded as they scroll into view.

<img class="lazy-image" data-src="/blog/images/lacroy.jpg" alt="enjoy la croy">

With Javascript, an event listener is set up on window load that collects all lazy-image class images and creates an intersection observer.

let images;
window.addEventListener("load", function(event) {
  images = document.querySelectorAll(".lazy-image");
  createObserver();
}, false);

The observer is attached to each target image. The callback function handleIntersect is executed whenever a target is observed to cross a certain threshold as defined in options. Here root: null refers to the viewport and has a margin around it of 0px. A threshold of 0.05 means that the callback is run when 5% of the target is visible within the viewport. The fallback behavior is to load all images at once.

function createObserver() {
  let observer;
  const options = {
    root: null,
    rootMargin: "0px",
    threshold: 0.05
  };
  if ('IntersectionObserver' in window) {
    observer = new IntersectionObserver(handleIntersect, options);
    images.forEach(image => {
      observer.observe(image);
    });
  } else {
    images.forEach(image => {
      image.src = image.dataset.src;
    });
  }
}

The intersectionRatio is a number between 0.0 and 1.0 that indicates how much of the target is visible within the root. The callback checks that the target is visible and sets src to the data-src to load the image. It also adds fade-in class to the image for a tiny animation effect and removes the observer. Since I’m only checking whether the image is visible in the viewport and do not need to know by how much, I could instead use the Boolean property entry.isIntersecting for the if statement.

function handleIntersect(entries, observer) {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0) {
      let img = entry.target;
      img.src = img.dataset.src;
      img.classList.add("fade-in");
      observer.unobserve(img);
    }
  });
}

And this is the CSS animation. The image fades in from invisible and moves up slightly as if sliding into view.

.fade-in {
  animation: fade-in 1.2s ease-in-out;
}
@keyframes fade-in {
  0% {top: 8px; opacity: 0;}
  100% {top: 0; opacity: 1}
}

You can see this lazy loader in action as you scroll through this blog.

Sando Club MVP

Registration for the Sixth Annual Sandwich Club Summit is now open! The Summit is free to attend but registration is required as seating is limited. I’m excited to be a presenter this year debuting an app that will help you find a friend with whom to share a sandwich at lunch.

In a recent conversation about bring products to market, I heard some advice that I shall paraphrase as: “Build for the ten people you can strong-arm into using your app, not the 10,000 users you may one day acquire.” I’ve been thinking about this, and about my “creative process” as it relates to “art” vs. “design”, and I’ve decided to both trim some features from the MVP and head in an aesthetic direction that would not lionize UX yet not sacrifice accessibility standards.

Originally, Ashley and I had considered user privacy a priority; one ought to be able to create a group visible only to known and invited participants. However, anticipating that just a handful of friends will be initial users, privacy logic seems less urgent a feature to implement. Frankly, even with a hundred users I would do away with privacy controls all together.

Does Sando Club need to be a single page app? I’m indifferent! But I’m bored of seeing the ubiquitous slick UI with full-bleed stock images and parallax effects (edit: lol). Insofar as I have an artistic style, it would favor low-concept graphics, camp, and the abject. I want to recreate the world in my image visually and conceptually, not imitate the designs de rigueur. So I’ve settled against using heavy frameworks and am proceeding with Rails, CSS grid for responsive layout, and a little Vue. And now to draw some sandwiches as avatars.

Marquee Direction="Up"

I wanted to replicate the obsolete html tag <marquee direction="up"> with css animation. Here is my demonstration on Codepen and you can see this effect in action in this post.

First, I wrapped the marquee block inside <div class="box"> so I can center this section on the page:

<div class="box">
  <div class="marquee-up">
    <p>
      😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎  😎
    </p>
  <div>
</div>

And in CSS I created and used a keyframe animation:

.box {
  height: 200px;
  width: 80%;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}
.marquee-up {
  width: 100%;
  position: absolute;
  text-align: center;
  animation: marquee-up 2.5s linear infinite;
}
@keyframes marquee-up {
  0% { top: 100%; }
  100% { top: -20px; }
}

Atlas Wrote Poetry

I just read this terrific article about the early computer programming career of the writer J. M. Coetzee. In the mid-1960s he had a hand in designing the Atlas 2 supercomputer in Britain. During off hours, he wrote programs to generate “computer poetry” using an algorithm to select and combine words from a closed vocabulary. The Harry Ransom Center at The University of Texas at Austin has a collection of Coetzee’s papers that includes sheaves of this “computer poetry” on dot matrix printouts from 1965. These papers were never published, but he did included some edited phrases in later writings. The archives also contain printouts of Coetzee’s Fortran programs and Fortran pseudocode.

Stick It

Continuing with neat CSS properties that also align with my naming scheme, I’d like to discuss position: sticky. Recently I worked on a layout for a site where I wanted to have a menu bar that would move with the page upon scroll and remain fixed once it hits the top. One can do this with JavaScript, but sticky takes care of it in just a couple of lines.

#menu {
  position: sticky;
  top: 0;
}

The element is treated as relatively positioned until it reaches a specific threshold—here when the menu hits the very top of the container—at which point its position becomes fixed. Safari requires position: -webkit-sticky and support for Edge is in development.

Canvas It

For a new project I wanted to make a responsive canvas element with an image overlaid with lines. To accomplish this, I put the canvas inside a section div that had width: 80vw. I used jQuery with this project and I called .parent() to get the canvas container, then set the height and width of the canvas as functions of its container. Because the container width is computed in viewport units, the canvas dimensions will also always be proportional to the size of the window.

const canvas = $('#canvas');
const ctx = canvas.get(0).getContext('2d');
const container = canvas.parent();

function resizeCanvas() {
  canvas.css('width', container.width());
  canvas.css('height', container.width() / 2 );
}

$(window).resize(resizeCanvas);

Skip It

I’d like to talk about an exciting CSS discovery I made this week: text-decoration-skip: ink. This property specifies where a text decoration should break. In the case of ink this means that the underline will skip and leave white space around descenders, the portion of a character that extends below the baseline of the font, and glyphs like semicolons. Though this property is reported by MDN, it is not yet supported by Firefox at the time of this writing. If you are using Chrome or Safari desktop, you can see this effect by hovering over a link on this page. It’s a minor change that improves the legibility of underlined text.

Sando Doodles

I have been noodling around with some graphics and design ideas for Sando Club app. I used the GreenSock Animation Platform to animate DOM elements with JavaScript. Previously, I worked with the GSAP Draggable plugin to create duncecapsforcats.com and on this project I’m using their TweenMax package. Most of my animations are built by transforming CSS values by position, rotation, or scale. GSAP facilitates these manipulations and gives several easing options that make the transitions smooth and interesting. There are also methods to yoyo or repeat the animations. They have a handy visualizer that you can use to see the effects of changes in values and durations. I like to play around with the variables until the timing looks just right and snappy. This is a good reason to watch cartoons for movement and transition ideas. As a starting point, I recommend the Bugs Bunny classic Hot Cross Bunny and anything in the oeuvre of Pepé le Pew.

Something Different

The Sixth Annual Sandwich Summit will take place on September 23, 2017, and this year I am honored to be serving on the planning committee.

The annual Sandwich Club Summit is a chance for fellow sandwich enthusiasts to come together and discuss pressing topics. The summit will include presentations, panels, workshops, a sandwich-themed exhibition and a grilled cheese bar. This year’s summit theme is “Something Different” and will focus on sides (chips, pickles, slaws, baked beans, beverage pairings, etc).

We are currently accepting proposals for presentations, workshop, or demos and Upper Crust, the official publication of Sandwich Summit. Proposals may entail anything concretely or marginally related to sandwiches and their accoutrements. The open call closes on June 15th.

My collaborator Ashley Robinson and I shall demo Sando Club, an app to help you find a friend with whom to share a sandwich lunch. I’d mentioned a sandwich app on this blog before, but this is a different product that I will discuss in future posts. We are very excited for its debut and hope you can join us at the Summit!

sandwich summit ad

Sounds Like Retina Display

After I finished writing my La-Croy Pattern Generator last week, I decided to add in the HTML5 canvas element so users may download the image as a wallpaper for their phone or computer.

Here are some things to consider when starting a canvas project. First, you ought to add a fallback behavior, e.g. an alternate image or text, directly between the opening and closing <canvas> tags. Browsers that do not support canvas will ignore the tags and display the fallback content.

It is a also good idea to include support for high pixel density (Retina) displays, especially if you will be using JPGs or PNGs, so your images will look crisp on those screens. (SVGs are scalable and will remain sharp on any display.) One way to do this is to size your assets at twice their display dimensions and then halve them in the browser with CSS or JS.

Another way is to set up the canvas from the start to support HiDPI displays. Apple’s canvas guide details how the backing store and pixel ratios work, and lists the steps to detect HiDPI displays and draw the canvas. First, check whether the browser has window.devicePixelRatio defined. If it is greater than one, the browser is open on an HiDPI screen.

function backingScale(context) {
  if ('devicePixelRatio' in window) {
    if (window.devicePixelRatio > 1) {
        return window.devicePixelRatio
    }
  }
  return 1
}

Retina devices have a pixel ratio of 2 because there is a 2:1 ratio of display pixels to backing store pixels in both the x and y direction. Standard-resolution displays, on the other hand, map one backing store pixel to one display pixel, so their device pixel ratio will always be 1.

I set up the canvas like so, using the newly determined device pixel ratio to transform the scale of the canvas accordingly:

  const canvas = document.querySelector('canvas')
  const ctx = canvas.getContext('2d')
  const scaleFactor = backingScale(ctx)
  ctx.scale(scaleFactor, scaleFactor)

I then drew the HiDPI canvas by multiplying the canvas pixel values by the device pixel ratio and set its width and height to the dimensions of the browser window. This would create a canvas larger than the window size and shrink it to fit for a higher number of pixels per inch.

function resizeCanvas() {
    let w = window.innerWidth
    let h = window.innerHeight
    if (scaleFactor > 1) {
      canvas.width = w * scaleFactor
      canvas.height = h * scaleFactor
      canvas.style.width = w + 'px'
      canvas.style.height = h + 'px'
      draw()
    } else {
      canvas.width = w
      canvas.height = h
      draw()
    }
  }

Lastly, I used the HTMLCanvasElement.toDataURL() method to generate a download link for the full pattern.

function download(link, filename) {
  link.href = canvas.toDataURL()
  link.download = filename
}
downloadButton.addEventListener('click', function() {
  download(this, 'livelacroix.png')
  }, false)

Enjoy LaCroix and try a La-Croy wallpaper!

Just Shufflin'

Last week I made a demonstration using images with prime-numbered dimensions to build a non-repeating background pattern. Then I decided to create a generator that would randomly select a number of images from my collection and change their display order to make a new wallpaper pattern.

enjoy la croy I <3 LaCroix

Here are some of the mechanics of the project. I made an array of image names and spliced it randomly to create a new array with a length randomly selected between a minimum of four and a maximum of the number of items in the original array.

const images = ['avocado', 'berry', 'cuke', 'flowers', 'grapefruit', 'peony', 'plain', 'pomelo', 'tangerine']
function randomize(array) {
  // Choose a random integer between two values, inclusive, i.e. minimum of 4 and maximum of number of items in original array: Math.floor(Math.random() * (max - min + 1)) + min
  let n = Math.floor(Math.random() * (array.length - 4 + 1)) + 4
  let randomizedImages = []
  // Iterating n times, splice an item at a random index and put it into a new array
  while (n) {
    image = array.splice(Math.floor(Math.random() * array.length), 1)
    randomizedImages.push("url('images/" + image.toString() + ".png')")
    n--
  }
  return randomizedImages
}

I researched whether splicing is the most efficient and unbiased method to randomize an array and came across the Fisher-Yates shuffle algorithm. This article by Mike Bostock has a good explanation and visual demonstration of the difference between the two methods. The animations on his page were made with D3.js, a JavaScript library for data visualization created by Mike Bostock. It shows what while random splicing is unbiased, at each iteration the elements in the original array must shift down to compact the array, resulting in O(n2) performance.

In contrast the Fisher-Yates algorithm has O(n) complexity because it would shuffle the elements of the array in place. The tail of the array is used to store the shuffled items while we draw only from the remaining elements at the head.

function shuffle(array) {
  let currentIndex = array.length, temp, randomIndex
  // While there remain elements to shuffle
  while (currentIndex) {
    // Select an element at random from the front (and decrement the bound between shuffled and untouched items)
    randomIndex = Math.floor(Math.random() * currentIndex--)
    // And swap it with the current element at the back
    temp = array[currentIndex]
    array[currentIndex] = array[randomIndex]
    array[randomIndex] = temp
  }
  return array
}

I also used the Fisher-Yates shuffle to select a random background color from an array of hex codes that would change with each new pattern. I further added the ability to generate a new pattern by clicking a button or by resizing the browser window. In order to avoid a strobing effect when the images and colors change, I limited the rate at which the resize function could be fired by using the debounce method from the Underscore.js library:

function debounce(func, wait, immediate) {
  let timeout
  return function() {
      let context = this, args = arguments
      clearTimeout(timeout)
      timeout = setTimeout(function() {
          timeout = null
          if (!immediate) func.apply(context, args)
      }, wait)
      if (immediate && !timeout) func.apply(context, args)
  }
}

Sounds Like Enjoy

At the last CSS Layout Club meeting, Jen Simmons suggested I look at using the “cicada principle” with CSS elements to create “random” designs. As an evolutionary strategy, certain cicadas emerge in prime-numbered intervals like thirteen or seventeen years. Since prime numbers may not be evenly divided into smaller integers, cicada life cycles become naturally asynchronous with those of predators that have a two-to-ten-year population cycle, leaving them immune. We can use this idea of prime-numbered intervals to generate seemingly random, non-repeating image patterns.

I probably drink three cans of LaCroix a day and I wanted to create something that reflects my love for this beverage. I made several PNG images with transparent areas and adjusted their dimensions to prime numbers, e.g. squares of 337px or 409px, etc. In the CSS I added multiple files to the background-image property, and the list order determined the layer order of the images starting with the top one at the forefront.

body {
  background-image:
  url('../images/berry.png'),
  url('../images/flowers.png'),
  url('../images/peony.png'),
  url('../images/grapefruit.png'),
  url('../images/avocado.png'),
  url('../images/pomelo.png'),
  url('../images/cuke.png'),
  url('../images/tangerine.png');
}

I played around with the dimensions to get the effect I wanted. I can imagine adjusting the opacity of the images to mix new colors where the tiles overlap. And even though the full pattern looks complex, the file sizes are fairly small. After compressing the images with TinyPNG, they ranged from 5kb to 20kb each. LaCroix, please send me soda!

Best Custom Piñatas in New York

A brief diversion: I’m delighted to announce that a pair of my custom piñatas are included in the Spring 2017 Real Weddings issue of Martha Stewart Weddings! The feature, A Colorful Brooklyn Wedding with Lots of Patterns, may be viewed online and my work is shown in slides 12 and 13.

You probably did not know that New York Magazine selected my work for “Best Custom Piñatas” in their Winter 2015 Weddings issue. Isn’t that something.

If you’d like to see my other work, I have an installation of textiles, prints, and ceramics in The Wassaic Project’s 2017 Summer Exhibition Vagabond Time Killers on view from May 20 to September 23.

The fat, flaccid flesh of a pig’s teat

Today I learned the Chinese characters nāngchuài 囊膪 that mean “the fat, flaccid flesh of a pig’s teat”. What does this have to do with coding, you ask. Nothing. However, hydrolyzed pig collagen is a featured ingredient in many Asian skincare products.

I’m an avid user of Asian skincare products and have been keeping a spreadsheet of all my purchases with a notable ingredients list and links to PubMed articles I’ve read while researching their efficacy. I wanted to find the best products for my skin type and keep a catalogue of the most effective active ingredients to reference when I’m deciding on a new purchase. Then I made the spreadsheet into a Rails app called Creamy. I saved my spreadsheet in comma separated values format and used the Ruby class ‘csv’ to seed my database with this file. This is the information I wanted for each product:

class CreateCreams < ActiveRecord::Migration[5.0]
  def change
    create_table :creams do |t|
      t.string :name
      t.string :brand
      t.decimal :price
      t.integer :size
      t.string :notes
      t.integer :times_purchased, default: 1
      t.integer :format_id
      t.boolean :favorite, default: false
      t.boolean :current_rotation, default: false
    end
  end
end

Using price and size, I wrote a method to find the cost per unit. And with times_purchased I can calculate the total sum of money I’ve spent on all these products which is displayed on the index page. With favorite and current_rotation, you can sort by which products I like the most and which I currently use in my skin care routine, denoted by “♥” and “☽” respectively.

In my database, I made a separate table for formats that lists the different types of products, e.g. daytime cream, nighttime cream, emulsion, sheet mask, clay mask, etc. My creams table has an attribute of format_id and in models I write a format has_many :creams and a cream belongs_to :format. In a recent conversation I was asked if a product can only be in one format, could not the format exist in an enum array as an attribute of the creams table instead of as a stand-alone table.

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. —Java Documentation

ActiveRecord::Enum is available in Rails since 4.1, and ClassyEnum is a gem you can use with Ruby. I can rewrite my cream model like so:

class Cream < ApplicationRecord
  enum format: [:essence, :mist, :emulsion, :gel]
  # And so on; there are currently 22 cream formats in my collection.
end

Or explicitly map the attribute and database integer with a hash:

class Cream < ApplicationRecord
  enum format: {essence: 0, mist: 1, emulsion: 2, gel: 3}
end

And my database declaration would have this attribute:

create_table :creams do |t|
  t.column :format, :integer, default: 0
end

I can see this may be useful for, for example, setting different statuses for users. After some consideration, I decided that enum is not the way to go in this case. My original design of the database allows that there are products in formats I have not tried or am not even aware of, and I need to be able to dynamically update the list of formats as I encounter them. To wit, I just bought this Enzymatic Exfoliation Powder from Skin Actives and it comes in a powder form that I have never used. I don’t know what enzymatic cleansing means and I’d normally research a product a bit more, but the novelty of it intrigued me. I’ll add this to the app once it arrives, so check back for the ingredients list and my notes. Currently, full CRUD actions are reserved for only me. I’ll be adding a search feature soon.

1 Weird Trick

This blog is written with Markdown. You’re probably wondering, “How did she make those cute image captions when Markdown doesn’t handle text formatting like that?” It’s a trick, you see.

kitten croissandwich Untitled (a kitten croissando)

![a kitten croissando](/images/croissando.jpg)
*Untitled (a kitten croissando)*

This Markdown syntax produces the following HTML:

<p>
  <img src="/images/croissando.jpg" alt="a kitten croissando">
  <em>Untitled (a kitten croissando)</em>
</p>

In your CSS file you can write img + em { } where the ‘+’ combinator selects the adjacent sibling of the specified element. Thus you can style all <em> tags immediately following <img> within the <p> block without changing other <em> tags across the page.

img + em {
  font: italic 69%/1.5em $content-font;
  display: block;
  text-align: center;
}

And if you write {display: block; text-align: center;} you can center that caption. Whee!

If You Select Something, See Something.

Highlight something on this page.

OMG PINK!

Let me tell you about the CSS pseudo-element ::selection. It allows you to change the default browser style for selected text. There are three properties you can use:

  • color
  • background
  • text-shadow

For this page I changed selected text to pink with no background color. You’ll need the vendor prefix for Firefox to recognize this selector.

::-moz-selection {
  color: $pink;
}
::selection {
  color: $pink;
}

You may have noticed the different colors when selecting list items, indicated by the “❥” symbol. I achieved this by writing ::selection for nth-child of <li> like so:

li:nth-child(n)::selection  {
  color: $lilacLite;
}
li:nth-child(2n)::selection {
  color: $periwinkle;
}
li:nth-child(3n)::selection {
  color: $lilac;
}

On selection, The first element in the list will turn a light lilac color, followed by every second element in periwinkle, and every third in a darker lilac. The colors will repeat in order as more items are added to the list. Pretty!

Fine Art, See?

For our React project, my team made an app that allows the user to curate her own museum exhibition. We decided to work with the Harvard Art Museums API because their collection comprises over 200,000 objects and 91% of those items have at least one image. However, whether the image url is publicly available is limited by rights restrictions, especially for modern and contemporary works still under copyright.

The experimental endpoint contains some interesting data, but does not cover all objects in the collection.

The data is a result of machine processing data and images through external services including Google Vision, Microsoft Cognitive Services, Imagga, and Clarifai. We are using the services to perform face detection, text detection, color analysis, and automatic feature detection/tagging of images. We have not created training sets for these services. We are feeding in our images and data as-is to see what comes out.

I found this image that is clearly a dog wearing a hat, shirt, and pants.

dog wearing a hat Untitled (poodle wearing pants, shirt, and suspenders) courtesy Harvard Art Museums

However, the response shows that the Microsoft service suggested captions: [{text: "a man sitting in the snow", confidence: 0.11904114499362317}] and Imagga analyzed the image as

categories: [
{
name: "events parties",
confidence: 79.74
},
{
name: "pets animals",
confidence: 6.55
},
{
name: "text visuals",
confidence: 3.57
},
{
name: "people portraits",
confidence: 2.57
},
{
name: "paintings art",
confidence: 2.21
},
{
name: "food drinks",
confidence: 1.9
},
{
name: "streetview architecture",
confidence: 1.54
},
{
name: "nature landscape",
confidence: 1.01
},
{
name: "interior objects",
confidence: 0.83
}
]

LOL! We found that Google Vision can be used to analyze a photograph for emotions, e.g. surpriseLikelihood: "VERY_UNLIKELY", and hats headwearLikelihood: "VERY_LIKELY", and can also determine the coordinates of facial features. There are a lot of attributes in the experimental endpoint that would be fun to play with, but for this project we decided to go with the object endpoint to return more reliable results based on user-generated keywords. Along with the standard catalogue descriptors, it has fields for provenance, accession year, and total page views that may yield some surprises.

I like to search the collection with mundane words like dog and pink. My two favorite images so far are:

white dog biting brown dog Untitled (white dog biting brown dog) courtesy Harvard Art Museums

bulldog in pink bathroom Untitled (bulldog in pink bathroom) courtesy Harvard Art Museums

Aren’t they just terrific? It turns out both photos are by Ann E. Wulff from her series Caged Tigers published in the early 80s. I love them so much.

A Variety of Hats to Put on Your Cat

Last week I worked with the GreenSock Animation Platform to make a little animation with a car in it. To research movement, I watched a lot of Pepé le Pew videos on YouTube followed by a viewing of Space Jam. Among other things I used GSAP to handle CSS property transformations, SVG objects, and easing in and out of transitions with different Bézier curves. After that I discovered the GSAP Draggable plugin that makes any DOM element draggable and spinnable. So of course I used it to make a game to put different headgear on my cat, Rufus. I made the layout with CSS grid and I didn’t write any feature queries so the site is best seen on the latest version of any browser. Here are the link and repo. I think it’s one of my best artworks to date and am very pleased.

dunce caps for rufus Dunce Caps for Rufus

Baby’s First CSS

This is the complete stylesheet from my very first homepage published to the Internet. The original HTML 4.0 files are unreadable, likely because they were written in MS Word and exported for web as HTML. This was a thing that one was once able to do. The layout was constructed with tables, frames, and indiscriminate use of

marquee.

That’s the marquee element in case your browser doesn’t support it, and it is animating the word marquee to scroll across the page. I’d also drawn a bunch of navigation buttons in varying monochromatic hues and drop shadow effects for hover and click transformations using javascript.

body {
  font-family: verdana, geneva, arial, helvetica, sans-serif;
  font-size: 13px;
  color: #333333;
}
a {text-decoration: none;}
a:link {color: #666666;}
a:visited {color: #999999;}
a:hover {color: #6699FF;}

img {border: none;}

input {
  font-family: verdana, geneva, arial, helvetica, sans-serif;
  color: #333333;
  border: thin solid #666666;
  font-variant: small-caps;
}

select {
  font-family: verdana, geneva, arial, helvetica, sans-serif;
  color:#333333;
  border: thin solid #666666;
  font-variant: small-caps;
}

textarea {
  font-family: verdana, geneva, arial, helvetica, sans-serif;
  color:#333333;
  border: thin solid #666666;
  font-variant: small-caps;
}

Lol, so basic. The site had an image gallery of my artwork and a little js quiz you could take to determine if we could be best friends irl. My cool friend, the first person I knew who had her own custom url, hosted it as a subdomain of her site. I’m still making dumb websites and I’ve become a huge CSS fangirl. There is so much you can do now to make interesting design without third party frameworks. I’m pretty tired of seeing the same bootstrapped websites with full screen images overlaid with sans-serif type and scroll animations that belie little aesthetic vision or substance. Give CSS a chance. Bring back marquee.

SVG...Svengali?

These puns are getting worse. I’m sorry.

I wanted to mention a couple of things not related grid that I learned about CSS while making my homepage. First, I used viewport units for some spacing and typography. One viewport unit is measured as 1/100th of the viewport size and can be height based (vh), width based (vw), or the smaller (vmin) or the larger (vmax) of the two. Thus the size of the element is a ratio of the window size and this is pretty handy for responsive design. I wrote one media query to slightly alter the layout and type size for smaller screens.

I also used inline svg images for my social icons. The nice thing about inline svg is that you can style it using CSS, circumventing the need for javascript or other libraries. For example, I can change the color of images with icon class by using the svg fill property and also animate the transition.

.icon {
  fill: rgba(176, 4, 44, 0.9);
  transition: fill 400ms ease;
  height: 24px;
}
.icon:hover {
  fill: rgba(255, 0, 23, 0.9);
  transition: fill 400ms ease;
}

The HTML markup will not look as clean because it contains all the paths to draw the object, but for a small project and this CSS enthusiast, inline svg is just the ticket.

If you use Adobe Illustrator, it’s easy to retrieve the code for your svg image. Prepare your vector graphic and save as svg. The svg options dialog box will appear and clicking on the button ‘SVG Code…’ will show the code that you can copy and paste into your HTML file. Alternatively, you can use SVG Optimiser to create the inline code or optimize what you already have.

Grid Your Lions for CSS Grid

I’m really excited about the CSS grid layout implementation that became widely available this month. I personally love the control of hand-coded layout. Imagine positioning elements using two dimensions instead of one and now you can throw out all your frameworks (but hang on to your Sass)!

Jen Simmons has been writing about and working with grid extensively and I’ve been learning from her demos and meetups. To dive into grid, I made a homepage using many of its new properties. Firefox Nightly has an inspector tool to help visualize grid layout. Herewith a few examples from my stylesheet.

Grid is not yet available in all browsers, so you ought to write a feature query to test whether the property is supported.

@supports (display: grid) {
  // code runs only if grid is supported //
}

I wrote CSS to use flex display if grid is not supported (not shown). I wanted a simple layout for my page: one column with multiple rows and plenty of whitespace to show off the beautiful background image. Here you can see I used grid-template-areas to name my row sections and grid-template-rows to define the height of each row using fractional units to calculate proportions. I added a gap of 6.66% between rows.

.container {
  display: grid;
  grid-template-columns: 1fr;  grid-template-rows: 2fr 1.5fr auto .75fr;
  grid-template-areas:
    "header"
    "content"
    "links"
    "footer";
    grid-gap: 6.66%;
}

Then, for the grid area “links”, I used a nested grid to generate boxes of a certain width to fill the width of a column. The browser computes how many full columns can fit within each row and automatically wraps overflow to the next line. Each list item will occupy one box in the grid. The columns are responsive to the viewport size and I do not have to write any media queries for this to be so.

.projects ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  grid-gap: 6.66%;
}

For the footer, I nested a grid for social media icons. Here I practiced using explicitly placed elements. First, I created a grid that has one row and five columns of equal width.

.social-icons {
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: repeat(5, 1fr);
  grid-gap: 5%;
}

Then I placed my email icon into the first row, first column, and GitHub link in the second column and so on.

.email {
  grid-row: 1 / 2;
  grid-column: 1 / 2;
}

.git {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
}

The numbers above are not fractions, rather they delineate the starting and ending line numbers of the column in which the element ought to be placed. This is great for placing elements in exact locations on the grid. You can see the line numbers using Firefox inspector tool, but currently this feature is only available with the Nightly build.

In the future, everyone will be world-famous for 15...people.

Garkov is a webcomic that uses Markov chains to generate text using old Garfield strips as its input corpus. Inspect element reveals that the text is produced using Markov chains on individual letters.

garkov

gorfold

What is a Markov chain?

A stochastic model describing a sequence of possible events in which the probability of each event depends only on the state attained in the previous event.

What is a Markov chain?

text screenshot

To use Markov chains to generate text, you would read the input corpus and for each word, note the word that follows it and at what frequency. You would output text by making each word a random function of its immediate predecessor. The number of previous words is the “order”. Order-1 text is generated by producing the next word as a random function of the current word. Order-2 generation produces the next word as a random function of the previous two-word grouping. And so on. Text generated using higher orders will more closely resemble natural speech.

and->I
I->ran
ran->I
I->ran
ran->so
so->far
far->away
away->I
I->just
just->ran
and I->ran
I ran->I
ran I->ran
I ran->so
ran so->far
so far->away
far away->I
away I->just
I just->ran
 

Illustrated Markov Chain

Output will depend on size of your input corpus — the larger the training dataset the better, although too large a sample size and your output will be exactly your input.

Markov chains can be used to generate text to bypass spam filters; model games of chance; certain statistical analysis; and even underlies Google’s PageRank algorithm:

The formula uses a model of a random surfer who gets bored after several clicks and switches to a random page. The PageRank value of a page reflects the chance that the random surfer will land on that page by clicking on a link. It can be understood as a Markov chain in which the states are pages, and the transitions are all equally probable and are the links between pages.

To write my own Markov chain text generator these are the likely steps:

  • Read input corpus.
  • Break input corpus into individual words using some kind of regex.
  • Be able to choose order, say one to four, and build a hash(?) storing the n-order word(s) prefix with its word suffix.
  • Select and output a prefix followed by its suffix.
  • In case a prefix has multiple possible suffixes, select one from that set randomly or weighted by statistical frequency.
  • Create new prefix-suffix pairs until output of predetermined length is completed.

Idea: input corpus is some kind of art theory book, output is your artist statement.

Sandwiches in the Expanded Field, or, “Happiness is...sharing a sandwich.”

Sando Club is about friendship. Members eat sandwiches and collect stamps together in exchange for fun and prizes. Sandwich Club is about documentation. Members record their best sandwich meals and comments in a Google Sheet. Sando Club is ephemeral; Sandwich Club is a database.

spreadsheet

You can access Sandwich Club’s Goggle Sheet via a link on their website. Anyone with the link can record sandwiches and comments. I saved this sheet as a comma-separated values (CSV) file and used Ruby’s CSV class methods to parse the file and seed the database for a Rails application. I wanted to preserve both the functions of the sheet and its ethos, i.e. anyone can access the database and add valid entries by code of honor, no user sign-up or login required.

splash page

In the app you can view a list of all the sandwiches, see individual sandwich entries, view each member and a list of all the sandwiches she has eaten, add a new sandwich, and leave comments for each sandwich.

add page

A particular challenge of adding a new sandwich is the ability to create a new eater and assign her to that sandwich. To do this I employed nested attributes.

class Sandwich < ApplicationRecord
  has_many :eater_sandwiches
  has_many :eaters, through: :eater_sandwiches
  has_many :comments
  validates :ingredients, :eaters, :tasting_notes, :date, presence: true
  accepts_nested_attributes_for :eaters
  accepts_nested_attributes_for :comments, reject_if: :blank_comment?

  private

  def blank_comment?(attributes)
    attributes[:text].blank? || attributes[:eater_id].blank?
  end
end

According to Rails documentation:

“Nested attributes allow you to save attributes on associated records through the parent. By default nested attribute updating is turned off and you can enable it using the #accepts_nested_attributes_for class method. When you enable nested attributes an attribute writer is defined on the model. The attribute writer is named after the association.”

In my example this means that a new method has been added to the model:

eater_attributes=(attributes)

And in the sandwiches controller I must whitelist the new associated params:

def sandwich_params
  params.require(:sandwich).permit(:ingredients, :date, :location, :price, :tasting_notes, eater_ids: [], eater_attributes: [:id, :name], comment_attributes: [:text, :sandwich_id, :eater_id])
end

My new sandwich form looks like this:

form_for @sandwich do |f|
  f.label :eater_ids, "Whom ate this?"
  f.select(:eater_ids, @eaters.map {|e| [e.name, e.id] }, {prompt: "Select one or many"}, {multiple: true, size: 8})

    f.fields_for :eater do |ff|
      ff.label :name, "Add an eater not on this list"
      ff.text_field :name
      ff.hidden_field :id
    end

  f.submit "Add Sandwich"
end

And for the sandwich controller’s create action:

@eater = Eater.find_or_create_by(name: params[:sandwich][:eater][:name])

In these snippets you can see that I also have nested attributes for comments. On an individual sandwich page you can create a new comment to associate with that sandwich and at the same time create a new user for that comment.

about sandwich club

The form for updating a sandwich will have a nested field for adding a new comment which in turn has a nested field for adding an eater.

form_for @sandwich do |f|
  f.fields_for :comment do |ff|
    ff.text_area :text, size: "150x2"
    ff.fields_for :eater do |fff|
      fff.label :name, "Your Name"
      fff.text_field :name
      fff.hidden_field :id
    end
  end
  f.submit "Add Comment"
end

I added to the comment model:

accepts_nested_attributes_for :eater

and whitelisted the attributes in the comment controller:

def comment_params
  params.require(:comment).permit(:text, :sandwich_id, :eater_id, eater_attributes: [:id, :name])
end

And in the sandwich controller’s update action I shall need:

@comment = Comment.new(text: params[:sandwich][:comment][:text], sandwich_id: @sandwich.id)

@eater = Eater.find_or_create_by(name: params[:sandwich][:comment][:eater][:name])

Nested attributes are not so terrible and you many never have to use them! The Sixth Annual Sandwich Club Summit will take place in September in Wassaic, NY.

“My Mother Was a Computer”

In my last post I wrote that I wanted to look at the relationship between programming language and natural language, maybe something about signifiers and the signified. I didn’t have a particular thesis or really any salient thought about the topic so I started googling a bunch of words I thought might go together. One book seemed to have a fair number of mentions and so I Amazon Primed My Mother Was a Computer: Digital Subjects and Literary Texts by N. Katherine Hayles (University of Chicago Press, 2005). Hayles is a professor of literature at UCLA and has written a lot about the dynamics of technology, science, and art.

If you Google Book API it, you will find it listed under the subject “Computers.” It’s description would be:

“We live in a world, according to N. Katherine Hayles, where new languages are constantly emerging, proliferating, and fading into obsolescence. These are languages of our own making: the programming languages written in code for the intelligent machines we call computers. Hayles’s latest exploration provides an exciting new way of understanding the relations between code and language and considers how their interactions have affected creative, technological, and artistic practices.

My Mother Was a Computer explores how the impact of code on everyday life has become comparable to that of speech and writing: language and code have grown more entangled, the lines that once separated humans from machines, analog from digital, and old technologies from new ones have become blurred. My Mother Was a Computer gives us the tools necessary to make sense of these complex relationships.”

I thought it was going to be a breezy read since I can read good and do other stuff good too, but it’s quite dense and extensively end-noted with references to other texts. Which is to say, especially with my current schedule, that I didn’t get much further than the prologue. However, there were a lot of interesting things in this reading that I can relay, and I will report on subsequent chapters throughout this blog. 

The first surprising thing I learned was related to the title itself. I thought Hayle might have been referring to some sci-fi trope about artificial intelligence or simulacra, and she probably does later, but in this usage it has historical significance. In the 1930s and 40s people, predominantly women, were employed to perform the clerical work of calculations and were called “computers.” Hayles lifted her title from a line in Anne Balsamo’s book Technologies of the Gendered Body (Duke University Press, 1995). Balsamo’s mother actually worked as a “computer” and she uses this family history “to launch a meditation on the gender implications of information technologies,” which seems another interesting text to explore apropos of current discussions around diversity in tech.

Hayle addresses the semantic shift of the sentence:

“For my purposes, the different implications of the sentence from World War II to the end of the twentieth century mark a shift from a society in which the intelligence required for calculations was primarily associated with humans to the increasing delegation of these labors to computational machines. The sentence stands, therefore, as a synecdoche for the panoply of issues raised by the relation of Homo sapiens to Robo sapiens, humans to intelligent machines.”

Here is another nougat of thought to chew on from the Prologue:

“For scientists making the strong claim for computation as ontology, computation is the means by which reality is continually produced and reproduced on atomic, molecular, and macro levels. In A New Kind of Science, Stephen Wolfram extends the claim to include biological systems and, indeed, complex behaviors of every kind, including social and cultural systems. In this context, “My mother was a computer” can be understood as alluding to the displacement of Mother Nature by the Universal Computer. Just as Mother Nature was seen in the past centuries as the source of both human behavior and physical reality, so now the Universal Computer is envisioned as the Motherboard of us all.”

Next time I will report on my original point of inquiry after I read Chapter 1 on Language and Code.

“I was three, sitting alone, humming a Russian children’s song to myself. In translation:

Little finch, little finch, where have you been?

—Down at the market, drinking vodka.

Hearing this performance, a visiting grown-up asked, did I know what vodka was? Now, the Russian for water is Vadá, and Vodka is its diminutive. So, being asked about vodka, I replied, “Eto málinkaya Vadá”—“it’s small water.” I confess to this story, because it signals an early tendency to address the signifier instead of the signified.”

—Leo Steinberg, False Starts, Loose Ends

I’m interested in the relationship between the semiotics of programming language and natural language, although I don’t have a cogent arguement right now.