Wednesday, June 12, 2024

Case Study: 84—24 | Codrops

Web DevelopmentCase Study: 84—24 | Codrops


Let me introduce myself. I’m Michele Giorgi, a 36-year-old Frontend dev from Rimini, Italy. I got into this gig back when being in the web game meant wearing multiple hats: designer, developer, and sometimes even copywriter, SEO specialist, marketing analyst, and sysadmin.

As the field evolved and roles became more specialized, I found myself leaning into coding, but I still think pigeonholing ourselves is a bit limiting. IMHO a solid web developer should dabble in design, and vice versa.

Alongside my day job, I’ve always had side projects going on. They’ve been my way of learning new stuff, sticking to that old-school one-man-band ethos. Over the past decade, I’ve been tinkering away on an iOS game, a personal website (never saw the light of day), a WordPress form plugin, and most recently, 84—24, the subject of this story.


So, despite WebGL being a long-standing trend in web design that’s here to stay, I kept putting off diving into the technology. Partly because I didn’t have the right project to start with, and partly because I was intimidated by the steep learning curve.

Then the perfect project came along when I decided to buy and fix up a 1984 Macintosh 128K, kind of like an early mid-life crisis move.

As I got into the restoration process, I realized I wanted to document and share the journey with others. To make it accessible to as wide an audience as possible, alongside the story, I wanted to showcase a 3D model of the Macintosh 128K. And it wasn’t just any model – it had to undergo a full teardown during the various restoration phases.

So, now all I had to do was “simply” learn how to:

  • create a 3D model of the Mac and its internal components
  • integrate it into a web page
  • animate it

Tech stack

Since my focus was on learning 3D modeling and its application in web contexts, I decided to keep my stack as straightforward as possible. I didn’t need any distractions from additional JS libraries, no DOM abstraction layers, no databases to query.

So, I started with a blank index.html file and used Parcel.js as my build tool.

  • posthtml – for markup and page templates
  • Markdown – for content
  • SCSS – for styling
  • JavaScript

Javascript dependencies

Even in these cases, I tried to keep my JavaScript dependencies to a bare minimum.

  • Three.js – for handling the 3D scene and as a WebGL renderer. (I know I mentioned learning WebGL, but life’s too short.)
  • GSAP – for managing animations and timelines.
  • Swiper – for sliders and main navigation.
  • ICS – for generating custom calendar events (ICS files) via JavaScript

Learning process

In the past, I’d dabbled in 3D stuff, but:

  • It had been over 8 years.
  • The context was different (Unity / iOS app).
  • My role was more of an “asset assembler” rather than a true creator, dealing with 3D models and animations made by others.

So, I decided to start from scratch with a specific course for the web realm, something I’d had bookmarked for ages: (and I can’t recommend it enough).

It’s a 91-hour video course, crafted by Bruno Simon, a true rockstar in the “creative development” scene. The course covers every aspect of developing a 3D web project, from the basics to 3D design and modeling in Blender, importing models into Three.js, advanced techniques, and integrating Three.js with React.

I quickly realized the sheer volume of topics was massive. If I waited until I mastered every technique as I wanted to, I’d probably take years to release my project. So, I decided to focus solely on what truly mattered at the moment. For my project, I opted for an extremely simple low-poly style, minimal lighting, avoiding complex baked textures, custom shaders, or any post-processing.

3D model

After a few solo attempts, I realized starting from a completely empty scene was impossible for me.

Drawing something aesthetically pleasing, while keeping details and polygon count to a minimum, and considering real proportions, was beyond my capabilities. It wasn’t just a technical limitation stemming from not yet mastering the editor, but an “artistic” limit in my personal ability to synthesize and represent what I see.

So, I decided to start with assets from a real 3D artist: Boris Cargo. On the 3D marketplace, I found his 3D model of the Macintosh case that perfectly encapsulated all the features I was looking for.

On this 3D model, I kept it simple:

  • Carved out the screen recess and floppy disk drive.
  • Isolated individual elements into separate meshes
    • Front case
    • Rear case
    • Ports

Finally, I had a solid foundation to work from.

While there were plenty of 3D models available for the external case, the same couldn’t be said for the internal computer components. The only reference I found was this work by d0rkmushr00m on Sketchfab. It was an amazing model, but unfortunately, it wasn’t downloadable or purchasable, and the voxel art style was completely different from what I wanted to develop.

However, I decided to take inspiration from the workflow used by the author, described in this article. Contrary to the approach suggested by any 3D modeling guide/course, I chose to design the internal components starting from a 2D environment more familiar to a web designer like Figma.

To my delight, I discovered that Blender could import SVG graphics while maintaining control over each layer/shape. So, I started with a photo of the motherboard from the teardown by iFixit and began replicating the most important components on the board.

Once I finished the logic board, I grouped all the identical elements and exported them as SVG. After importing the SVG into Blender, I turned all the circles into meshes and added the third dimension to each component using the extrude tool.

I went through the same process with all the internal components until I replicated every part of the computer. Then, I grouped all the elements into the fewest possible meshes to minimize the polygon count, and exported the model in GLB format.

Style and typography

I went for a super minimal and raw style, aiming to prioritize content over container.

The website itself should behave more like a “system reader” than a site with its own distinct style. So, I opted for:

  • A really simple layout with just 3 alternating section layouts.
  • Standard vertical scroll navigation.
  • Default system sans-serif font for the body text.

The only contrast to this extreme minimalism was the typography for the headings. I went with EB Garamond with slightly reduced letter-spacing, a Google font very similar to Apple Garamond, which Apple has widely used in communication since 1984.

I know to many (real) designers, pairing it with the default sans-serif (like Helvetica on MacOS/iOS) might seem “forced”, but to me, this contrast perfectly encapsulates Apple’s evolution over the past 40 years.

Animations and transitions

Once I imported the GLB model with three.js, animating individual elements became a breeze thanks to GSAP. Just grab the element to animate from the 3D model using the getObjectByName method, passing the mesh name you defined in Blender during design as a variable.

let scene = new Scene(model)
let mac = scene.modelGroup.getObjectByName('mac')

In my case, I linked the playback of each animation to scrolling, defining a timeline for each section. To simplify the definition of each section, I created a helper function that initializes a timeline with some default values used for all sections.

let sectionTimeline = (section, callback, options={}, defaults={}, custom=()=>{}) => {
  let tl = new gsap.timeline({
      onUpdate: el.scene.render,
      scrollTrigger: {
          trigger: '#' + section,
          scrub: true,
          start: 'clamp(top bottom)',
          end: 'clamp(top top)',
      defaults: {
          duration: 1,
          ease: 'power2.inOut'

Then, I defined a timeline with the elements and their respective properties to animate for each section.

//01 Introduction
sectionTimeline('introduction', (tl) => {
  tl.fromTo(mac.position, {x: 60, y:-40}, {x: -80, y:-47, ease: 'power1.out'}, 0)
  tl.fromTo(mac.rotation, {y: 0.12}, {y: -5.7, ease: 'power1.out'}, 0)
//02 Right
sectionTimeline('right', (tl) => {
  tl.fromTo(mac.position, {x: -80}, {x: 80, ease: 'power1.Out'}, 0)
  tl.fromTo(mac.rotation, {y: -5.7}, {y: -0.5, ease: 'power1.Out'}, 0)

As for the Wireframe transition, all credit goes to Steve Garner and his demo: Airplanes. Here too, the method is simple and involves creating a new group containing only the elements that will “transform” into wireframe.

For each mesh, its geometry needs to be extracted and a new LineSegments element needs to be created, defined on a different layer/view (1) from the default visible one (0).

const case = model.getObjectByName('case')
let wireframe = case.clone() = 'wireframe'
wireframe.children = []
case.traverse((mesh) => {
  if(mesh.isMesh) {
    let edges = new THREE.EdgesGeometry(mesh.geometry)
    let line = new THREE.LineSegments(edges)
    line.material.depthTest = false
    line.material.opacity = 1
    line.material.lineWidth = 1
    line.material.color = new THREE.Color(0x000000)
    line.material.transparent = true

Now that we have the 3D model on layer 0 and the wireframe on layer 1, it’s just a matter of showing/hiding the desired view. To sync the transition to scrolling in a section of the page’s DOM, just modify the “height” and “bottom” properties of the view with GSAP.

gsap.fromTo(el.scene.views[1], { height: 0, bottom: 0 }, {
  height: 1,
  bottom: 0,
  ease: 'none',
  onUpdate: el.scene.render,
  scrollTrigger: {
    trigger: '#footer',
    scrub: true,
    start: 'top bottom',
    end: 'top top'

To keep things simple, I wrapped up these techniques in a small demo on Codepen.

See the Pen
84-24 demo by michelegiorgi (@michelegiorgi)
on CodePen.light

In the demo, I included a simplified version of the 3D model used on, excluding the case (since it’s a commercial 3D model).


I’m particularly proud of this element, which combines a loader and a non-conventional slider navigation. At first glance, it looks like a header with a transparent background, but it actually has a background color that changes from section to section and only covers the text, not the 3D scene.

These features might sound like they involve complex logic, but in reality, the solution is quite simple and relies solely on a combination of z-index and sticky elements.

  • Header – z-index: 10
  • WebGL scene – z-index: 3
    • Covers the entire window but isn’t clickable
  • Pseudo-element for each section – z-index: 2
    • Section’s background color
    • Position sticky
    • Exact height of the header
  • Section content – z-index: 1

Mobile first maybe a day

I know, I know… it’s 2024, and 70% of global internet traffic comes from mobile devices.

But this website is more of a style exercise than a best-practices reference for usability. It was conceived from the get-go to be viewed on a wide landscape device. Creating a mobile version has always been on the ideal to-do list, but it would have taken a lot of time and would have ended up being a greatly simplified version of the experience offered on tablets/desktops.

As often happens with personal projects, there’s the risk of endlessly chasing the idea of a “perfect project”, which can stretch out timelines and lead to losing interest in the project itself. So, at the beginning of the year, I decided to set myself a deadline, coinciding with the anniversary of the release of the first Macintosh, on January 24th.

To meet that deadline, I decided to launch with a landscape-only version and prepare two specific “gate pages” to block visitors coming from:

  • Smartphones
  • Portrait-oriented tablets

“Better done than perfect.”

Author Unknown

Acknowledgments and Thanks

84—24 snagged the FOTD on FWA and the SOTD + Developer Award on Awwwards, some of the most prestigious international web design awards. Getting these accolades for a personal project was truly rewarding, and I couldn’t have felt more honored until I received the offer to write a dedicated article on Codrops.

I want to take this opportunity to thank once again all the people already mentioned on the website and Manoela, who gave me this opportunity.

For any questions, feel free to reach out to me at

Inspirational Websites Roundup #58

Check out our other content

Check out other tags:

Most Popular Articles