SIMD.js is cool. You can use it with Firefox or Edge nightly.
Can we speed up rectangle intersection checks? Yes we can! …With a big caveat.
class Rectangle {
  constructor(x1, y1, x2, y2) {
    this.items = new Float32Array(4)
    this.items[0] = x1
    this.items[1] = y1
    this.items[2] = x2
    this.items[3] = y2
  }
  get x1() { return this.items[0] }
  get y1() { return this.items[1] }
  get x2() { return this.items[2] }
  get y2() { return this.items[3] }
}
function rectIntersectsSIMD(r1, r2) {
  const a = SIMD.Float32x4.load(r1.items, 0)
  const b = SIMD.Float32x4.load(r2.items, 0)
  // r1.x1 r1.y1 r1.x2 r1.y2    r2.x1 r2.y1 r2.x2 r2.y2
  // 0     1     2     3        4     5     6     7
  const c = SIMD.Float32x4.shuffle(a, b, 0, 1, 4, 5)
  const d = SIMD.Float32x4.shuffle(a, b, 6, 7, 2, 3)
  const maskLT = SIMD.Float32x4.lessThanOrEqual(c, d)
  return SIMD.Bool32x4.allTrue(maskLT)
}
Results
Benchmark run using jsperf on Firefox Nightly 53.0a2 (2017-01-30).
How It Works
The basic rectangle check logic is as follows:
function rectIntersectsScaler(r1, r2) {
  return r1.x1 <= r2.x2 &&
         r1.y1 <= r2.y2 &&
         r1.x2 >= r2.x1 &&
         r1.y2 >= r2.y1
}
This is equivalent to:
function rectIntersectsScaler(r1, r2) {
  return r1.x1 <= r2.x2 &&
         r1.y1 <= r2.y2 &&
         r2.x1 <  r1.x2 &&
         r2.y1 <  r1.y2
}
Properly speaking, we can’t do much with this. Yes yes we could compute r1.x1 <= r2.x2 && r1.y1 <= r2.y2 and r2.x1 < r1.x2 && r2.y1 < r1.y2 with separate SIMD operations (and indeed, we would have to do this if we wanted 64-bit coordinates), but I’m doubtful that this would be worth it.
Let’s try a slight tweak:
function rectIntersectsScaler(r1, r2) {
  return r1.x1 <= r2.x2 &&
         r1.y1 <= r2.y2 &&
         r2.x1 <= r1.x2 &&
         r2.y1 <= r1.y2
}
Wait, what?
Yeah, it’s wrong. It’ll yield a false positive if r2.x1 == r1.x2 or r2.y1 == r1.y2. This is OK for my purposes, but it means this won’t be a drop-in replacement.
Now then.
const a = SIMD.Float32x4.load(r1.items, 0)
const b = SIMD.Float32x4.load(r2.items, 0)
This loads the items Float32Array into SIMD registers (if the JIT wants to, anyway).
// r1.x1 r1.y1 r1.x2 r1.y2    r2.x1 r2.y1 r2.x2 r2.y2
// 0     1     2     3        4     5     6     7
const c = SIMD.Float32x4.shuffle(a, b, 0, 1, 4, 5)
const d = SIMD.Float32x4.shuffle(a, b, 6, 7, 2, 3)
This is the interesting part. The shuffle function lets us rearrange our data so that the we will compare c[0] <= d[0], c[1] <= d[1], etc.
const maskLT = SIMD.Float32x4.lessThanOrEqual(c, d)
return SIMD.Bool32x4.allTrue(maskLT)
Now we just compare c <= d, and make sure that all bits in the resulting mask are 1.
Caveats
rectIntersectsSIMD() yields a false positive if r2.x1 == r1.x2 or r2.y1 == r1.y2. This is because the < is transformed into <= so we can use a single SIMD op.
In practice, you’re probably better served by minimizing memory management overhead. Having an explicit Rectangle object is, uh, wasteful.
