DEV Community

Cover image for CSS Backrooms
Ben Evans
Ben Evans

Posted on • Edited on

CSS Backrooms

No Images - No JavaScript - Only HTML and too much CSS!

In January, I watched the very cool YouTube documentary about how The Backrooms came to exist: https://www.youtube.com/watch?v=o3cTIn2Z_Ck

Original 4Chan post

The original 4Chan post that started the sensation 👆

Because of this, I fell in love with The Backrooms' origin story and had the idea of building on my previous 3D maze, mapping out the rooms to fit the photograph, and, this time, with the determination to make it work on Safari!

Original 3D maze

So I set to work.

As a proof of concept, I expanded the maze from a 7x7 grid to a 12x12, made everything beige, adding a ceiling and some wallpaper to the walls. Then I lined up the camera angle with the original 4Chan post's image. All looked good; it was probably possible! But knowing full well that I'd made it even less likely to work on Safari, the next step was to try and solve that issue.

From trying to fix the original, I knew that Safari didn't like the sheer size in pixels of the old maze. If I made the rooms tiny and if not too much went out of view, then it almost worked. So this time I scaled everything down. I made each 'room', or square of the grid, only 3x3rem; this helped, but it still crashed. Only when I narrowed down the path from a 12x12 grid to a 4x12 grid did things start to work.

I then spent the next three months working on a formula (I'm not very good at maths) to work out the position of the camera on the grid and remove cells that were not in view.

This is the formula to mark the position, not actually required for the final thing:

    %test {
        background: rgba( magenta,.9);
    }

    @for $x from 1 through $grid {
        &:has(#x-#{$x}:checked) {
            @for $y from 1 through $grid {
                &:has(#z-#{$y}:checked) {
                    $yn1: ($y - 1);
                    $sum: ($yn1 * $grid + $x);

                    floor:nth-of-type(1) tile:nth-of-type(#{$sum}) {
                        @extend %test;
                    }
                }
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

And it worked on Safari! 🥳

I then tidied everything up, and in doing so, learned a few useful things. Because of the size of these grids, I needed to make everything more efficient. When it came to mapping out the levels, I got this down to a fine art. I created @each loops, so I could easily just add a batch of coordinates from the map.

$east: ( 16: 4 2, 20: 8 2, 28: 4 3, 31: 7 3, 33: 9 3, 40: 4 4, 43: 7 4, 46: 10 4, 47: 11 4, 49: 1 5, 59: 11 5, 61: 1 6, 64: 4 6, 67: 7 6, 73: 1 7, 76: 4 7, 79: 7 7, 82: 10 7, 83: 11 7, 88: 4 8, 94: 10 8, 95: 11 8, 106: 10 9, 107: 11 9, 109: 1 10, 112: 4 10, 118: 10 10, 119: 11 10, 121: 1 11, 124: 4 11, 129: 9 11, 131: 11 11 );

@each $key, $values in $east {
    $first-value: nth($values, 1);
    $second-value: nth($values, 2);

    body:has(#level-1:checked):has(#x-#{$first-value}:checked):has(#z-#{$second-value}:checked):has(.x-rotation:checked) {
        .downb {
            @extend %d-none-i;
        }

        .downb[for="x-0"] {
            @extend %d-block-i;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Getting the first and second values was super handy and simpler than I had imagined.

I also had to pay attention to how the SASS compiled the output and discovered the beautiful benefit of @extend. I probably should have already known this, but now I really understand.

And if, like me, you don't already know:

A loop would normally compile to each loop listing one after the other, for example, this:

z floor tile{
    $short-wood-right: 1, 2, 3;

    @each $i in $short-wood-right {
        &:nth-of-type(#{$i}) span:before {
            transform: translate3d(2.97rem, .5rem, 5.5rem);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Would output like this:

z floor tile:nth-of-type(1) span:before {
    transform: translate3d(2.97rem, .5rem, 5.5rem);
}
z floor tile:nth-of-type(2) span:before {
    transform: translate3d(2.97rem, .5rem, 5.5rem);
}
z floor tile:nth-of-type(3) span:before {
    transform: translate3d(2.97rem, .5rem, 5.5rem);
}
Enter fullscreen mode Exit fullscreen mode

Whereas by using @extend, this:

%wood {transform: translate3d(2.97rem, .5rem, 5.5rem);}
z floor tile{
    $short-wood-right: 1, 2, 3;
    @each $i in $short-wood-right {
        &:nth-of-type(#{$i}) span:before {
            @extend %wood;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Would output like this:

z floor tile:nth-of-type(1) span:before, z floor tile:nth-of-type(2) span:before, z floor tile:nth-of-type(3) span:before {
    transform: translate3d(2.97rem, .5rem, 5.5rem);
}
Enter fullscreen mode Exit fullscreen mode

Which is much lighter to load, especially with as many loops as I am using.

So I tidied everything up, improved the wallpaper, added two floors, put some wall furnishings in, created some kind of storyline with a beginning and end, and added some scary elements. It didn't seem quite complete without any sound, so I threw together some atmospheric loops and, much as it goes against the whole CSS only thing, I had to use a tiny bit to play the sounds 😬 but I made this the absolute bare minimum:

<script>t = i => document.querySelectorAll('audio').forEach(a => { a.id == i ? (a.paused && a.play()) : (a.pause(), a.currentTime = 0) })</script>
Enter fullscreen mode Exit fullscreen mode

If anyone can get that to be any smaller, then please let me know.

In the end, the CSS was so big that I couldn't fit the uncompiled SASS version into CodePen, so it's just the outputted CSS. And I think I added back in so much detail that it probably doesn't work on iPhones anymore. But, anyway, here it is; please let me know what you think I could do to improve the gameplay or the code, and the first person to share a screenshot of the end wins! Good luck in there 😱

Top comments (3)

Collapse
 
alvaromontoro profile image
Alvaro Montoro

This is amazing!

Collapse
 
dhanjeerider profile image
Dhanjee rider

🐃

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

This is extremely impressive, the amount of effort you put in is obvious. I feel the pain of Safari issues so much