DISK 04 / MODE7

a racetrack seen from just above it, turning as you drive round: one flat picture of a track, and every line of the screen below the horizon a slice of it, nearer and bigger the lower it is. it is how the Super Nintendo did its racing and flying games with no 3D at all

a floppy disk out of the vending machine, run by pino on the computer at the internet cafe, with lower down. Mode 7, the Super Nintendo's background mode (Nintendo, 1990); written again for the cafe.

function setup(ctx, W, H) {
var k = { cam: 10, speed: 0.006, dusk: 0 }
Object.assign(k, {"cam":6})
var n = 256, tex = new Uint8Array(n * n * 3)
for (var y = 0; y < n; y++) {
  for (var x = 0; x < n; x++) {
    var dx = x - 128, dy = y - 128, r = Math.sqrt(dx * dx + dy * dy)
    var col
    if (r > 70 && r < 96) {
      col = [70, 70, 76]
      if (Math.abs(r - 83) < 1 && Math.floor(Math.atan2(dy, dx) * 12) % 2) col = [230, 230, 220]
      if (Math.abs(dy) < 3 && dx > 70) col = (Math.floor(x / 3) + Math.floor(y / 3)) % 2 ? [250, 250, 250] : [20, 20, 20]
    } else if ((r > 66 && r <= 70) || (r >= 96 && r < 100)) {
      col = Math.floor(Math.atan2(dy, dx) * 30) % 2 ? [220, 40, 40] : [240, 240, 240]
    } else {
      col = (Math.floor(x / 16) + Math.floor(y / 16)) % 2 ? [60, 150, 60] : [50, 135, 52]
    }
    var i = (y * n + x) * 3
    tex[i] = col[0]; tex[i + 1] = col[1]; tex[i + 2] = col[2]
  }
}
var w = 200, h = 150
var c = document.createElement('canvas')
c.width = w
c.height = h
var cx = c.getContext('2d')
return { k: k, n: n, tex: tex, w: w, h: h, c: c, cx: cx, img: cx.createImageData(w, h), ang: 0 }
}
function update(state, t, W, H) {
state.ang += state.k.speed
}
function render(ctx, state, t, W, H) {
var k = state.k, w = state.w, h = state.h, n = state.n, tex = state.tex, d = state.img.data
var horizon = 46
var a = state.ang
var px = 128 + Math.cos(a) * 83, py = 128 + Math.sin(a) * 83
var head = a + Math.PI / 2
var fx = Math.cos(head), fy = Math.sin(head), sx = -fy, sy = fx
for (var y = 0; y < h; y++) {
  for (var x = 0; x < w; x++) {
    var i = (y * w + x) * 4
    if (y <= horizon) {
      var g = y / horizon
      d[i] = k.dusk ? 40 + 160 * g : 90 + 90 * g; d[i + 1] = k.dusk ? 20 + 80 * g : 140 + 70 * g; d[i + 2] = k.dusk ? 70 + 40 * g : 220; d[i + 3] = 255
      continue
    }
    var z = k.cam * 30 / (y - horizon)
    var side = (x - w / 2) / w * z * 1.2
    var u = Math.floor(px + fx * z + sx * side) & (n - 1)
    var v = Math.floor(py + fy * z + sy * side) & (n - 1)
    var j = (v * n + u) * 3
    var fog = Math.max(0.35, 1 - z / 180)
    d[i] = tex[j] * fog; d[i + 1] = tex[j + 1] * fog; d[i + 2] = tex[j + 2] * fog; d[i + 3] = 255
  }
}
state.cx.putImageData(state.img, 0, 0)
ctx.imageSmoothingEnabled = false
ctx.drawImage(state.c, 0, 0, W, H)
}

more of these