#JavaScript (ES6), 5858 52 bytes
Builds the string recursively.
f=(n=64)=>n--?f(n)+(!n|n&15?'':` `)+(n&(1<<n>>(n>>4))?1:0&1):'' ###How it works
This recursion is based on the fact that the pattern is made of the vertical binary representation of nibbles 0x0 to 0xF:
0101010101010101 bit #0 <- Y = 0 0011001100110011 bit #1 0000111100001111 bit #2 0000000011111111 bit #3 <- Y = 3 ---------------- 0123456789ABCDEF ^ ^ X = 0 X = 15 Therefore, each position (X,Y) in this pattern can be expressed as the Y-th bit of X: X & (1 << Y). We can also isolate this bit with: (X >> Y) & 1. Rather than keeping track of X and Y, we iterate on a single variable n ranging from 0 to 63. So, the formula becomes: n & (1n <<>> (n >> 4)) & 1. It's actually easier to iterate from 63 to 0, so the string is built in reverse order. In other words, character N-1n-1 is appended to the left of character Nn.
As a side note, recursion doesn't bring anything here except shorter code.
Without the linebreaks, the code is 35 bytes long:
f=(n=64)=>n--?f(n)+(n&(1<<n>>(n>>4))?1:0&1):'' We need 17 more bytes to insert the linebreaks. This could be shortened to 14 bytes if a leading linebreak is acceptable.
###Demo
let f=(n=64)=>n--?f(n)+(!n|n&15?'':` `)+(n&(1<<n>>(n>>4))?1:0&1):'' console.log(f());