chrisphan.com

6 7

An analog clock reading 12:30 

Categories: computers, fun, programming

Yeah, if you have or frequently interact with children, you are probably familiar with the nonsense saying six seven. Is this a ridiculously silly meme? Of course it is, but so is (for example) trying to trick people into watching a particular music video. Watching my first-grader experience her first meme is wonderful and I choose to celebrate it!

Therefore, without further ado, I present:

67 67 67 six seven six seven six seven 67 67 six seven six seven 67 six seven

A version on its own page

Source code

In case you are interested, below is the source code for this animation.

Standalone HTML

This is the page linked above.

index.html HTML
<!doctype html>
<html lang="en">
  <!--
  Silly* six-seven animation

  Christopher Phan <https://chrisphan.com/>
  2026-W01

  For Emmy

  SPDX-License-Identifier: MIT

  *This whole six-seven thing is silly, but so is finding 1000 creative ways to
   trick someone into watching the same Rick Astley video :-) -->
  <head>
    <title>Six seven</title>
    <meta charset="utf-8" />
    <link href="reset.css" rel="stylesheet" />
    <link href="animate.css" rel="stylesheet" />
  </head>

  <body>
    <svg
      width="800"
      height="500"
      xmlns="http://www.w3.org/2000/svg"
      id="67_animation"
      aria-label="The number 67 and the words 'six seven' in various fonts and colors move horizontally from right to left at differing speeds."
    >
      >
      <defs>
        <path
          d="M 0,0 v -40 a 50 50 180 0 0 -100 0 v 120
                a 50 50 180 0 0 100 0 v -19 h -20 v 14
                a 30 30 180 0 1 -60 0 v -20
                a 30 30 180 0 1 60 0 h 20 v -5 a 40 40 90 0 0 -40 -40
                q -40 0 -40 10 v -60 a 30 30 180 0 1 60 0 v 30 z"
          id="six"
        />
        <path
          d="M 0,0 h 150 v 25 c -30,0  -70,30 -70,70 v 30 h 50 v 20 h -50
                v 75 h -25 v -75 h -50 v -20 h 50 v -35 c 0,-30 30,-50 40,-60
                h -110 v -25 z  "
          id="seven"
        />
        <g id="six-seven">
          <use href="#six" x="100" y="90" style="transform: scale(0.2)" />
          <use href="#seven" x="135" y="2" style="transform: scale(0.2)" />
        </g>

        <path
          id="disp_seg_vert"
          d="M 0,0 l 20,20 v 50 l -20,20 l -20,-20
                    v -50 l 20,-20 "
        />
        <g id="disp_seg_horiz">
          <use href="#disp_seg_vert" style="transform: rotateZ(-90deg)" />
        </g>

        <g id="6_lcd">
          <use href="#disp_seg_horiz" x="5" y="-5" />
          <use href="#disp_seg_vert" />
          <use href="#disp_seg_vert" y="100" />
          <use href="#disp_seg_vert" x="100" y="100" />
          <use href="#disp_seg_horiz" x="5" y="95" />
          <use href="#disp_seg_horiz" x="5" y="195" />
        </g>

        <g id="7_lcd">
          <use href="#disp_seg_horiz" />
          <use href="#disp_seg_vert" x="95" y="5" />
          <use href="#disp_seg_vert" x="95" y="100" />
        </g>

        <g id="67_lcd">
          <use href="#6_lcd" />
          <use href="#7_lcd" x="150" />
        </g>

        <pattern
          id="background_6_7"
          width="80"
          height="80"
          patternUnits="userSpaceOnUse"
          patternContentUnits="userSpaceOnUse"
        >
          <rect fill="black" x="0" y="0" width="80" height="80" />
          <use href="#six-seven" fill="#333" style="transform: scale(0.5)" />
          <use
            href="#67_lcd"
            fill="#333"
            x="400"
            y="25"
            style="transform: scale(0.1)"
          />
          <use
            href="#67_lcd"
            fill="#333"
            y="400"
            x="25"
            style="transform: scale(0.1)"
          />
          <use
            href="#six-seven"
            fill="#333"
            x="80"
            y="80"
            style="transform: scale(0.5)"
          />
        </pattern>
        <text class="label1" id="67_1">67</text>
        <text class="label2" id="67_2">67</text>
        <text class="label3" id="67_3">67</text>
        <text class="label1" id="67_4">six seven</text>
        <text class="label2" id="67_5">six seven</text>
        <text class="label3" id="67_6">six seven</text>
        <text class="label4" id="67_7">67</text>
        <text class="label5" id="67_8">67</text>
        <text class="label4" id="67_9">six seven</text>
        <text class="label5" id="67_10">six seven</text>
        <text class="label6" id="67_11">67</text>
        <text class="label6" id="67_12">six seven</text>
        <rect
          class="background"
          id="bg"
          x="-80"
          y="-80"
          width="960"
          height="660"
          fill="url(#background_6_7)"
        />
      </defs>
      <style>
        .label1 {
          font: 60px sans-serif;
        }
        .label2 {
          font: 120px serif;
        }
        .label3 {
          font: 90px monospace;
        }
        .label4 {
          font: 100px cursive;
        }
        .label5 {
          font: 75px fantasy;
        }
        .label6 {
          font: 130px system-ui;
        }
      </style>
    </svg>

    <script src="animation_67.js"></script>
  </body>
</html>

Typescript

I wrote the main script in TypeScript. It needs to be transpiled down to JavaScript before being used.

animation_67.ts TypeScript
/* @filename: animation_67.ts
 *
 * Silly* six-seven animation
 *
 * Christopher Phan <https://chrisphan.com/>
 * 2026-W01
 *
 * For Emmy
 *
 * SPDX-License-Identifier: MIT
 *
 * *This whole six-seven thing is silly, but so is finding 1000 creative ways to
 *  trick someone into watching the same Rick Astley video :-)
 *
 */


class AnimatedElt {
    // Represents a `use` element in an SVG that we will be moving around
    elt: SVGUseElement
    rootSvg: SVGElement

    constructor(elt: SVGUseElement, rootSvg: SVGElement) {
        this.elt = elt
        this.rootSvg = rootSvg
    }

    create(
        targetSVG: SVGElement,
        x: number,
        y: number,
        href: string,
        id: string,
        fill: string
    ): AnimatedElt {
        const elt = document.createElementNS('http://www.w3.org/2000/svg', 'use')
        elt.setAttribute('x', `${x}`)
        elt.setAttribute('y', `${y}`)
        elt.setAttribute('id', id)
        elt.setAttribute('fill', fill)
        elt.setAttribute('href', `#${href}`)
        targetSVG.appendChild(elt)
        return new AnimatedElt(elt, targetSVG)
    }


    move(dx: number, dy: number) {
        if (dx !== 0) {
            const currentX = this.elt.getAttribute('x')
        let newX = dx
            if (currentX !== null) {
                newX += parseFloat(currentX)
            }
            this.elt.setAttribute('x', `${newX}`)
        }
        const currentY = this.elt.getAttribute('y')
        if (dy !== 0) {
            let newY = dy
            if (currentY !== null) {
                newY += parseFloat(currentY)
            }
            this.elt.setAttribute('y', `${newY}`)
        }
    }
}

class Mover {
    velocityX: number
    velocityY: number
    item: AnimatedElt
    lastUpdate: Date

    constructor(velocityX: number, velocityY: number, item: AnimatedElt) {
        this.velocityX = velocityX
        this.velocityY = velocityY
        this.item = item
        this.lastUpdate = new Date()
    }

    create(
        targetSVG: SVGElement,
        x: number,
        y: number,
        href: string,
        id: string,
        fill: string,
        velocityX: number,
        velocityY: number
    ): Mover {
            const item = AnimatedElt.prototype.create(
                targetSVG, x, y, href, id, fill
            )
            return new Mover(velocityX, velocityY, item)
    }

    update() {
        const now = new Date()
        const elapsedTime = (now.valueOf() - this.lastUpdate.valueOf()) / 1000
        this.lastUpdate = now
        this.item.move(this.velocityX * elapsedTime, this.velocityY * elapsedTime)
    }
}

function randomColor(): string {
    const hue = Math.floor(Math.random() * 360)
    const sat = Math.random() * 100
    const light = Math.random() * 75 + 25
    return `hsl(${hue}, ${sat}%, ${light}%)`
}

/* The SVG should have the following elements defined:
 * - 67_lcd
 * - background_6_7 (pattern)
 * - 67_n for n = 1 to 12
 *
 * For example: https://chrisphan.com/67_animation/
 */

function createNew(n: number, targetSVG: SVGElement): Mover {
    const y = Math.random() * 500
    let c = n
    if (n > 13) {
        c = Math.floor(Math.random() * 14)
    }
    let href: string = ""
    if (c === 0) {
        href = "six-seven"
    } else if (c === 13) {
        href = "67_lcd"
    } else  {
        href = `67_${c}`
    }
    const id = `mover_${n}`
    const fill = randomColor()
    const velocityX = - (Math.random() * 50 + 50)
    return Mover.prototype.create(targetSVG, 850, y, href, id, fill, velocityX, 0)
}

function update(
    targetSVG: SVGElement,
    objects: Array<Mover>,
    active: Array<number>,
    startTime: Date,
    count: number,
    lastAdded: Date | null,
    inactive: Array<number>) {
    const now = new Date()
    const elapsedTime = (now.valueOf() - startTime.valueOf()) / 1000
    let sinceLastAdded = 1000000
    if (lastAdded !== null) {
        sinceLastAdded = (now.valueOf() - lastAdded.valueOf()) / 1000
    }
    let newCount = count
    let addedOne = false
    if (elapsedTime > newCount && sinceLastAdded > 0.5) {
        if (objects.length < 60) {
            const newThing = createNew(count, targetSVG)
            objects.push(newThing)
            active.push(objects.length - 1)
            addedOne = true
        } else if (inactive.length > 0) {
            const recycledIndex = inactive.shift()
            const recycled = objects[recycledIndex]
            recycled.item.elt.setAttribute('x', `${850}`)
            recycled.lastUpdate = new Date()
            active.push(recycledIndex)
            addedOne = true
        }
        if (addedOne) {
            newCount++
            lastAdded = new Date()
        }

    }
    let newActive: Array<number> = []
    active.forEach((idx) => {
        objects[idx].update()
        if (objects[idx].item.elt.getAttribute('id') === 'back') {
            if (parseFloat(objects[idx].item.elt.getAttribute('x')) > 80) {
                objects[idx].item.elt.setAttribute('x', '0')
            }
            if (parseFloat(objects[idx].item.elt.getAttribute('y')) > 80) {
                objects[idx].item.elt.setAttribute('y', '0')
            }
            newActive.push(idx)
        }
        else if (parseFloat(objects[idx].item.elt.getAttribute('x')) > -1000) {
            newActive.push(idx)
        } else {
            inactive.push(idx)
        }
    })

    window.requestAnimationFrame(
        () =>
        update(
            targetSVG,
            objects,
            newActive,
            startTime,
            newCount,
            lastAdded,
            inactive)
    )
}


const mainSvg = document.querySelector("svg[id='67_animation']") as SVGElement
let background = Mover.prototype.create(mainSvg, -80, -80, "bg", "back", "#333", 5, 5)
let objects: Array<Mover> = [background]
let active: Array<number> = [0]
const startTime = new Date()
update(mainSvg, objects, [0], startTime, 0, null, [])