Ollin

Alphabet

a type-specimen sheet of A–Z and 0–9 in a square-module font, after the shape-built alphabets of the book's "Squares" chapter. Each glyph is a 5×7 grid of drawRect squares. The squares shimmer on a traveling wave, and a red accent sweeps through the glyphs.

Run
swift run --package-path Examples Example-Recreations-OsamuSato-Alphabet
Source
Examples/Recreations/OsamuSato/Alphabet/Sketch.swift
//  Recreation after Osamu Sato — "The Art of Computer Designing: A Black and
//  White Approach" (1993, Graphic-Sha). A homage to its shape-built typefaces —
//  the modular square alphabets of Chapter 3 ("Squares", e.g. the "OS KAKU BOLD"
//  specimen) — where every letter is assembled from identical squares, set in
//  bold black with a red accent. An original Ollin interpretation: the letterforms
//  are our own, drawn from `drawRect` square modules, not ported from the book's
//  disk. A homage, not a reproduction, and not affiliated with or endorsed by the
//  artist.

import Foundation
import Ollin

/// A type-specimen sheet — A–Z and 0–9 — in a square-module font, after Osamu
/// Sato's shape-built alphabets. Each glyph is a 5×7 grid of square modules drawn
/// with `drawRect`. Sato's pages are still; here the sheet is alive: every square
/// shimmers (its size eased by a wave traveling diagonally across the whole
/// sheet), and a red highlight sweeps through the glyphs like a moving accent.
@main
final class Alphabet: Sketch {
    @Param(0...1) var shimmer = 0.6    // how much the squares pulse
    @Param(0.2...4) var sweepSpeed = 1.8   // how fast the red accent travels

    let sato = Color(red: 0.85, green: 0.09, blue: 0.16)
    let ink = Color.black

    let order = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

    /// 5×7 square-module letterforms (rows top to bottom, `#` = a filled square).
    let glyphs: [Character: [String]] = [
        "A": [".###.", "#...#", "#...#", "#####", "#...#", "#...#", "#...#"],
        "B": ["####.", "#...#", "#...#", "####.", "#...#", "#...#", "####."],
        "C": [".####", "#....", "#....", "#....", "#....", "#....", ".####"],
        "D": ["####.", "#...#", "#...#", "#...#", "#...#", "#...#", "####."],
        "E": ["#####", "#....", "#....", "####.", "#....", "#....", "#####"],
        "F": ["#####", "#....", "#....", "####.", "#....", "#....", "#...."],
        "G": [".####", "#....", "#....", "#.###", "#...#", "#...#", ".####"],
        "H": ["#...#", "#...#", "#...#", "#####", "#...#", "#...#", "#...#"],
        "I": ["#####", "..#..", "..#..", "..#..", "..#..", "..#..", "#####"],
        "J": ["#####", "...#.", "...#.", "...#.", "#..#.", "#..#.", ".##.."],
        "K": ["#...#", "#..#.", "#.#..", "##...", "#.#..", "#..#.", "#...#"],
        "L": ["#....", "#....", "#....", "#....", "#....", "#....", "#####"],
        "M": ["#...#", "##.##", "#.#.#", "#.#.#", "#...#", "#...#", "#...#"],
        "N": ["#...#", "##..#", "#.#.#", "#..##", "#...#", "#...#", "#...#"],
        "O": [".###.", "#...#", "#...#", "#...#", "#...#", "#...#", ".###."],
        "P": ["####.", "#...#", "#...#", "####.", "#....", "#....", "#...."],
        "Q": [".###.", "#...#", "#...#", "#...#", "#.#.#", "#..#.", ".##.#"],
        "R": ["####.", "#...#", "#...#", "####.", "#.#..", "#..#.", "#...#"],
        "S": [".####", "#....", "#....", ".###.", "....#", "....#", "####."],
        "T": ["#####", "..#..", "..#..", "..#..", "..#..", "..#..", "..#.."],
        "U": ["#...#", "#...#", "#...#", "#...#", "#...#", "#...#", ".###."],
        "V": ["#...#", "#...#", "#...#", "#...#", "#...#", ".#.#.", "..#.."],
        "W": ["#...#", "#...#", "#...#", "#.#.#", "#.#.#", "##.##", "#...#"],
        "X": ["#...#", "#...#", ".#.#.", "..#..", ".#.#.", "#...#", "#...#"],
        "Y": ["#...#", "#...#", ".#.#.", "..#..", "..#..", "..#..", "..#.."],
        "Z": ["#####", "....#", "...#.", "..#..", ".#...", "#....", "#####"],
        "0": [".###.", "#...#", "#..##", "#.#.#", "##..#", "#...#", ".###."],
        "1": ["..#..", ".##..", "..#..", "..#..", "..#..", "..#..", ".###."],
        "2": [".###.", "#...#", "....#", "...#.", "..#..", ".#...", "#####"],
        "3": ["#####", "...#.", "..#..", "...#.", "....#", "#...#", ".###."],
        "4": ["...#.", "..##.", ".#.#.", "#..#.", "#####", "...#.", "...#."],
        "5": ["#####", "#....", "####.", "....#", "....#", "#...#", ".###."],
        "6": ["..##.", ".#...", "#....", "####.", "#...#", "#...#", ".###."],
        "7": ["#####", "....#", "...#.", "..#..", ".#...", ".#...", ".#..."],
        "8": [".###.", "#...#", "#...#", ".###.", "#...#", "#...#", ".###."],
        "9": [".###.", "#...#", "#...#", ".####", "....#", "...#.", ".##.."],
    ]

    override func setup() {
        noStroke()
    }

    override func draw() {
        background(Color(white: 0.96))
        let g = grid(columns: 6, rows: 6, padding: .all(width * 0.06))
        // 5 modules wide, 7 tall, with a little padding inside each cell.
        let module = min(g.cellWidth / 6.0, g.cellHeight / 8.5)
        let glyphW = 5 * module, glyphH = 7 * module

        // One cell per glyph, in order; the inner loops walk each glyph's bitmap.
        for (cell, char) in zip(g.cells, order) {
            guard let pattern = glyphs[char] else { continue }
            let originX = cell.frame.x + (g.cellWidth - glyphW) / 2
            let originY = cell.frame.y + (g.cellHeight - glyphH) / 2

            // A red accent that sweeps diagonally across the grid over time.
            let onAccent = sin(Double(cell.column + cell.row) * 0.6 - time * sweepSpeed) > 0.45
            fill(onAccent ? sato : ink)

            for (my, line) in pattern.enumerated() {
                for (mx, bit) in line.enumerated() where bit == "#" {
                    // The square's size eases on a wave traveling across the sheet,
                    // keyed to its absolute module position, so the shimmer flows
                    // glyph-to-glyph instead of resetting per cell.
                    let phase = Double(cell.column * 6 + mx + cell.row * 8 + my) * 0.35 - time * 3.0
                    let frac = 0.80 + 0.14 * shimmer * sin(phase)
                    let cx = originX + (Double(mx) + 0.5) * module
                    let cy = originY + (Double(my) + 0.5) * module
                    drawRect(center: Vector2(cx, cy), width: module * frac, height: module * frac)
                }
            }
        }
    }
}

↑↓ move ↩ open