Are You Circumventing Change?

Learn more about developing leadership traits.

I had two part-time jobs while I attended college.  The first was in the computer lab for the College of Business Administration.  The second was working as a sound engineer at a local recording studio.  I can't stress enough how important both of these jobs were for me — providing life lessons that were on par with the lessons I learned in earning my degree.

How to Measure Technical Debt and How to Keep Track of the Progress

Don't drown in technical debt!

Nothing in this world is perfect. Neither is code. Sometimes the pressure to deliver fast results leads to coding shortcuts. Sometimes developers can’t find the right solution immediately and sometimes they are too lazy to do that. One day they misread the requirements, and the other – the requirements are changed in the process. But what does it have in common with the technical debt?

You may also like: Technical Debt: What It Is, Why It's Important, and How to Prioritize It

How Often Should You Update Your Mobile App?

Do you know?

For Android, iOS, and other apps, release frequencies vary. Managed app testing leader Testlio recently queried more than 75K client release records, covering a diverse range of industries (including Commerce, Education, Entertainment, Finance, Productivity, Sports, and Travel). Collectively, these Testlio clients have a user base of more than 1.5 billion people.

Smashing Monthly Roundup: What’s New?

Smashing Monthly Roundup: What’s New?

Smashing Monthly Roundup: What’s New?

Iris Lješnjanin

With the year slowly coming to an end, now is probably a great time to slow down and be mindful. Look back, reflect, breathe. It’s been a long year for all of us, so why not make yourself a good cuppa coffee or tea (whatever your preference is, there are other options, of course), and think about what your personal highlights were and set out some hopes and goals for the upcoming year to come.

Advent calendarsWe enjoyed counting down the days of 2019 with a good number of creative advent calendars that were brought to life by some quite talented folks. Some are publishing traditional articles while others have thought of a challenge for each day of the month of December. You can follow the lovely projects via their RSS feeds (if available) and Twitter accounts to make it easy for you to keep track of your favorites.

With the holidays coming up, why not get yourself cozy and catch up with a few talks? We have a whole lot of videos that you may like watching and listening to:

Photo of Sara SoueidanSara Soueidan presented a talk on Applied Accessibility in SmashingConf NYC, and Marcy Sutton spoke about Garbage Components. In case you’d like to follow along more closely, you’ll find the talk slides and some helpful links on the SmashingConf website, as well as some lovely snapshots of the event.

If you’re already planning which events to attend next year, we have an oveview of upcoming conferences around the globe that you may want to check out, and if you’re eager not to miss out on one of our SmashingConfs, then super early-bird SmashingConf tickets are already available! Just sayin’! 😉

What’s New At Smashing?

Smashing Podcast moderated by Drew McLellanIn case you missed it, we launched the Smashing Podcast just a few weeks ago — a bi-weekly podcast that is moderated by our dear friend and colleague, Drew McLellan. There are already 5 podcasts to listen to, so join him as he chats to Jina Anne about design tokens, Heydon Pickering on inclusive components, and Jason Pamental on all things variable fonts. You can subscribe and tune into any podcast player of your choice!

Inclusive Components book cover

Also, we officially released the “Inclusive Components” book, and the response has been overwhelmingly positive! Ari Stiles collected some of the book reviews we’ve received so far, with more coming in each day! Grab your own copy of Heydon’s book, and let us know what you think — we’d love to hear from you!

We publish a new article every day, and so if you’re not subscribed to our RSS feed or follow us on social media, you may miss out on some brilliant articles! Here are some that our readers seemed to enjoy and recommend further:

Best Picks From Our Newsletter

We’ll be honest: Every second week, we struggle with keeping the Smashing Newsletter issues at a moderate length — there are just so many talented folks out there working on brilliant projects! So, without wanting to make this monthly update too long either, we’re shining the spotlight on the following projects:

Note: A thank you to Cosima Mielke for writing and preparing these posts!

Web Almanac 2019

Take data processed from nearly 6 million websites and 85 people volunteering countless hours planning, researching, and writing — that’s what it took to create the 2019 edition of the Web Almanac, HTTP Archive’s annual state of the web report.

Illustration showing little characters made up of geometrical forms painting a website.
(Source)

The report consists of 20 chapters spanning aspects of page content, user experience, publishing, and distribution to shine a light on the current state of the ever-evolving network of technology that the open web is. A great resource to become more aware of current best practices.

How To Read A WebPage Test Waterfall Chart

Do you have difficulties reading WebPageTest waterfall charts? You’re not alone, it can be quite a challenge to remember the details and what they all mean. To freshen up your knowledge, Matt Hobbs collected all the many bits of information in a single blog post that we all can refer to.

WebPageTest timeline and chart
(Source)

The post explains the basic layout of the waterfall chart, what each of the colored vertical lines means, and what metrics the horizontal blocks refer to. It also lists common patterns that you might stumble upon in a waterfall chart. One for the bookmarks.

Open-Source Illustrations Kit

100-day challenges are a wonderful opportunity to dive deep into a topic or craft and evolve and improve with each day. Back in 2016, Vijay Verma spent almost two hours a day for 100 days designing, illustrating, and experimenting to get himself to the next level of illustration.

A screenshot of open-source illustrations available
(Source)

After living on a harddrive untouched since then, Vijay now decided to release the illustrations as a free open-source illustrations kit so you can use them for your landing pages, mobile apps, presentations, or whatever else comes to your mind. Available in AI, SVG, PNG, and EPS formats. Thank you, Vijay, for sharing!

30 Days Of Code Tidbits

Who doesn’t love a bite-sized tip? One that doesn’t take long to swallow but teaches you something new to instantly ease your life as a developer? Using the hashtag #codetidbits30 on Twitter, Samantha Ming posts a new coding tidbit every day in December.

Code tidbit explaining how to merge arrays
(Source)

Three ways to remove array duplicates, a little trick to style elements that have no children or text at all, and a solution for displaying your data in your browser dev tools, these are only some of the tips in the series. Covering JavaScript, HTML, and CSS snippets, #codetidbits30 is a true treasure chest of front-end goodies. Be sure to follow along.

Scaling SVGs Made Simple

Scaling <svg> elements can be a daunting task, since they act very differently than normal images. Amelia Wattenberger came up with an ingenious comparison to help us make sense of SVGs and their special features: “The <svg> element is a telescope into another world.”

A person looking through a telescope watching a star shape
(Source)

Based on the idea of the telescope, Amelia explains how to use the viewBox property to zoom in or out with your “telescope”, and, thus, change the size of your <svg>. A small tip that works wonders.

Recreating Print Layouts With CSS

When it comes to creative layouts, magazines are an endless source of inspiration. And thanks to CSS Grid, there’s nothing to hold you back from bringing more sophisticated layouts to the web, too.

Layout for an article abou the Dutch soccer player Virgil Van Dijk
(Source)

Inspired by magazine layouts, their use of typography and their structures, Dan Davies took on the challenge to recreate some of the print work he liked on the web. The result is an awe-inspiring collection of nine layouts that use the potential of CSS Grid to its fullest. Beautifully art-directed and responsive, they are great examples of pushing the limits of what’s possible on the web layout-wise.

Web Performance Vs. User Engagement

It’s no secret that performance can have a positive impact on user engagement and, in effect, improve conversion. To find out how performance correlates to conversion for their product, the team at Vrbo implemented an automated process that shows the connection between business events and performance data.

A screenshot of the holy grail dashboard created at Vrbo
(Source)

Carlos Moro from Vrbo now shares a case study in which he gives more insights into the approach, as well as handy tips for measuring site performance, user engagement, and putting the two into relation to one another. Interesting.

Time-Travel-Debugging For The Web

An early Firefox DevTools experiment that is worth keeping an eye on is Web Replay. Web Replay records your actions so you can track bugs down faster and understand your code better — a collaborative time-travel debugging instrument, so to say.

Replay in Firefox DevTools
(Source)

The replaying process preserves all the same JS behavior, DOM structures, graphical updates, and most other behavior that occurred while recording. Want to give it a try? Replay is already available in Firefox Nightly for macOS (still disabled by default until it is more stable, but you can turn it on manually). Handy!

Commit-Message-Driven Development

Have you ever considered to write the commit message before you start writing the code? Sven Hofmann does it this way, and now he explains why you could give it a try, too.

Bash functions incorporating commit-message-driven development into a developer’s workflow.
(Source)

We all know those vague and messy commit messages like “bugfixes and minor improvements” that aren’t helpful in the long term — especially if you’re working with a team or on an open-source project. The commit-message-driven workflow that Sven suggests could help change that: first, you write the commit message, then the code, then you commit. Having the scope of the task nailed down in advance, gives each commit a precise goal that you can focus on and that makes it easier to review your commits later on. Clever!

Dealing With Ads In 2020

Ads are a two-sided sword: nobody really likes them but a lot of sites depend on them to generate revenue. Working for a news company that is dependent on ads, Christian Schaefer wanted to find ways to minimize their impact and make them less annoying. Now he summarized his approach in a comprehensive blog post.

Screenshot from the “Dealing with Ads in 2020” blog post
(Source)

The post shares valuable insights into how Christian and his team developed a generic solution to transform and combine mobile and desktop ad code into one responsive ad loading code, how they improved performance by lazy loading the ads, what they did to prevent the ads from breaking the site’s layout, and some other things that add up to bringing the front end into a much better position when dealing with ads. Great tips for everyone who finds themselves wrangling ads.

If you don’t get our newsletter yet, then sign up here to receive useful techniques and goodies (including a free eBook on accessibility)!

From Smashing With Love

A month can be a long time to stay on top of things, so please do subscribe to our bi-weekly newsletter and our podcast if you still haven’t. Each and every issue is written and edited with love and care. No third-party mailings or hidden advertising — promise!

You can also tune into our very own Smashing TV, and follow us on Twitter, Facebook as well as LinkedIn. Please do always feel free to reach out and share your projects with us! We love hearing from you!

On behalf of the entire team, we wish you all the best for 2020! Stay smashing! 😉

Smashing Editorial (cm, vf, ra, il)

How to Enable Automatic Updates in WordPress for Major Versions

Do you want to enable automatic updates for major WordPress updates?

Automatic updates are enabled for minor releases on WordPress. This means that the WordPress.org team can automatically install security updates without requiring user input.

However, it does not automatically update your website when there is a new major release. Luckily, you can easily turn on automatic updates for major releases as well.

In this article, we’ll show you how to enable automatic updates in WordPress for major versions.

How to Enable Automatic Updates in WordPress for Major Versions

How WordPress Automatic Updates Work

The automatic updates feature was introduced in WordPress 3.7. This allowed WordPress to automatically install new minor releases to improve the security of your WordPress website.

There is an option to disable automatic updates in WordPress. However, we recommend that you keep automatic updates enabled because they usually address crucial security issues and vulnerabilities.

Now if you just run one or two WordPress websites, then you can simply follow our guide to safely update your WordPress site when there is a new major WordPress release. However, updating WordPress manually can be time-consuming if you manage multiple sites.

Luckily, managed WordPress hosting providers like WP Engine automatically update WordPress for all new releases, not just minor ones.

You can also enable auto-updates on a shared hosting provider like Bluehost and SiteGround. But first, you’ll just need to make sure that you have a proper backup system in place in case something goes wrong.

With that being said, let’s take a look at how to easily set up automatic updates for major WordPress releases. Here’s what we’ll cover in this tutorial:

Preparing for Automatic Updates in WordPress

The most important layer of security you can add to any website is to set up a backup system. Whether you turn on automatic updates or not, you should always have an automatic backup system in place for every WordPress website.

There are several helpful WordPress backup plugins that you can use to set up automatic backups on your WordPress site.

We recommend using Duplicator because it is the best WordPress backup plugin on the market, and it’s free. Duplicator allows you to easily set up automatic backups of your complete WordPress website.

It also allows you to automatically store your backup files in a remote location such as Google Drive or Dropbox or Amazon S3.

Once you have set up automatic WordPress backups, you can go ahead and turn on automated WordPress updates for major releases.

Method 1: Enable Automatic Updates for Major Releases From Dashboard » Updates

When you visit the Dashboard » Updates page in your WordPress admin area, you will see the message, ‘This site is automatically kept up to date with maintenance and security releases of WordPress only.’ These are the minor releases we mentioned above.

Enable Automatic WordPress Updates

If you would like all WordPress updates to be handled the same way, then simply click the link labeled ‘Enable automatic updates for all new versions of WordPress.’

Now major WordPress releases will be automatically installed as well.

If you decide to turn off automatic updates in the future, then simply return to the Dashboard » Updates page and click the link that says ‘Switch to automatic updates for maintenance and security releases only.’

Disable Automatic WordPress Updates Except Security Updates

Now major WordPress versions will not be installed automatically, just minor releases and security updates.

Method 2: Enable Automatic WordPress Updates for Major Releases Using a Plugin

The plugin method gives you more control over what is updated on your site. For example, it includes options to automatically update WordPress core, plugins, themes, and more.

First, you need to install and activate the Easy Updates Manager plugin. For more details, see our step-by-step guide on how to install a WordPress plugin.

Upon activation, you need to visit Dashboard » Updates Options page to set up the plugin.

Enabling Automatic Updates With a Plugin

Under the ‘Quick configuration actions’ section, you should click the ‘Custom’ button. After that, click the ‘Auto update all releases’ button under the ‘WordPress core updates’ section.

Note: Be cautious clicking the ‘Auto update everything’ button under ‘Quick configuration actions’. This will turn on automatic updates for everything, including WordPress core, plugins, themes, and translations.

The plugin will automatically store your settings and enable the major WordPress releases to be automatically updated.

If you would also like to use this plugin to automatically update your plugins and themes, then see our detailed guide on how to better manage automatic WordPress updates.

Method 3: Manually Enable Automatic Updates for Major Releases in WordPress

This method requires you to add code to your WordPress files.

First, you need to add the following line of code to your site’s wp-config.php file.

define( 'WP_AUTO_UPDATE_CORE', true );

There is one little problem with this code. It also enables what are called ‘nightly’ updates, or ‘nightlies.’ These are still under development and may contain bugs, so should not be installed on a live WordPress website.

To disable nightly builds and development updates, you need to add the following code to your theme’s functions.php file, or to a using a code snippets plugin such as WPCode.

add_filter( 'allow_dev_auto_core_updates', '__return_false' );
Adding a Text Snippet to WPCode

This filter will disable automatic updates for nightly builds or development updates.

Your WordPress site is now ready to automatically update itself without your input whenever there is a new WordPress version available.

Frequently Asked Questions about WordPress Automatic Updates

1. Why do I need to install WordPress updates?

WordPress is a regularly maintained software. Thousands of developers contribute to making WordPress better and more secure.

You need to install WordPress updates as soon as they are available. This ensures that your website has the latest security patches, new features, and the best speed and performance.

2. Are updates safe for my website?

As the world’s most popular website builder, WordPress updates immediately become available to millions of websites. The core team works very hard to ensure that they are absolutely safe for all websites to install.

However, we recommend everyone always back their WordPress website before updates. This allows you to quickly revert back in case anything goes wrong after an update.

3. Can I also automatically update WordPress plugins?

By default, WordPress requires you to manually install plugin updates. However, you can enable automatic updates for plugins as well.

See our guide on how to enable automatic updates for WordPress plugins.

4. Can I install updates on all my websites from a single dashboard?

By default, you’ll need to log in to each WordPress website to install updates. Luckily, you can use tools to manage multiple WordPress sites. These tools make it easier to install updates on all your WordPress sites without having to log in to each site.

Learn more in our guide on how to manage multiple WordPress sites from one dashboard.

We hope this article helped you learn how to enable automatic updates in WordPress for major releases. You may also want to learn how to choose the best web design software, or see our list of email marketing services for small business.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Enable Automatic Updates in WordPress for Major Versions first appeared on WPBeginner.

How to Create the Apple Fifth Avenue Cube in WebGL

In September 2019 Apple reopened the doors of its historic store in the Fifth Avenue and to celebrate the special event it made a landing page with a really neat animation of a cube made of glass. You can see the original animation in this video.

What caught my attention is the way they played with the famous glass cube to make the announcement.

As a Creative Technologist I constantly experiment and study the potential of web technologies, and I thought it might be interesting to try to replicate this using WebGL.

In this tutorial I’m going to explain step-by-step the techniques I used to recreate the animation.

You will need an intermediate level of knowledge of WebGL. I will omit some parts of the code for brevity and assume you already know how to set up a WebGL application. The techniques I’m going to show are translatable to any WebGL library / framework.

Since WebGL APIs are very verbose, I decided to go with Regl for my experiment:

Regl is a new functional abstraction for WebGL. Using Regl is easier than writing raw WebGL code because you don’t need to manage state or binding; it’s also lighter and faster and has less overhead than many existing 3d frameworks.

Drawing the cube

The first step is to create the program to draw the cube.

Since the shape we’re going to create is a prism made of glass, we must guarantee the following characteristics:

  • It must be transparent
  • Cube internal faces must reflect the internal content
  • The cube edges must distort the internal content

Front and back faces

In order to get what we want, at render time we’ll draw the shape in two passes:

  1. In the first pass we’ll draw only the back faces with the internal reflection.
  2. In the second pass we’ll draw the front faces with the content after being masked and distorted at the edges.

Draw the shape in two passes means nothing but calling the WebGL program two times, but with a different configuration. WebGL has the concept of front facing and back facing and this gives us the ability to decide what to draw turning on the culling face feature.

With that feature turned on, WebGL defaults to “culling” back facing triangles. “Culling” in this case is a fancy word for “not drawing”.

WebGL Fundamentals
// draw front faces
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);

// draw back faces
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);

Now that we have gone through the part of setting up the program, let’s start to render the cube.

Coloured borders

What we want to obtain is a transparent shape with coloured borders. From a flat white cube, in the first step we’ll add the rainbow color and then we’ll mask it with the borders:

First of all create the GLSL function that returns the rainbow:

const float PI2 = 6.28318530718;

vec4 radialRainbow(vec2 st, float tick) {
  vec2 toCenter = vec2(0.5) - st;
  float angle = mod((atan(toCenter.y, toCenter.x) / PI2) + 0.5 + sin(tick), 1.0);

  // colors
  vec4 a = vec4(0.15, 0.58, 0.96, 1.0);
  vec4 b = vec4(0.29, 1.00, 0.55, 1.0);
  vec4 c = vec4(1.00, 0.0, 0.85, 1.0);
  vec4 d = vec4(0.92, 0.20, 0.14, 1.0);
  vec4 e = vec4(1.00, 0.96, 0.32, 1.0);

  float step = 1.0 / 10.0;

  vec4 color = a;

  color = mix(color, b, smoothstep(step * 1.0, step * 2.0, angle));
  color = mix(color, a, smoothstep(step * 2.0, step * 3.0, angle));
  color = mix(color, b, smoothstep(step * 3.0, step * 4.0, angle));
  color = mix(color, c, smoothstep(step * 4.0, step * 5.0, angle));
  color = mix(color, d, smoothstep(step * 5.0, step * 6.0, angle));
  color = mix(color, c, smoothstep(step * 6.0, step * 7.0, angle));
  color = mix(color, d, smoothstep(step * 7.0, step * 8.0, angle));
  color = mix(color, e, smoothstep(step * 8.0, step * 9.0, angle));
  color = mix(color, a, smoothstep(step * 9.0, step * 10.0, angle));

  return color;
}

#pragma glslify: export(radialRainbow);

Glslify is a node.js-style module system that lets us split GLSL code into modules.

https://github.com/glslify/glslify

Before going ahead, let’s talk a bit about gl_FragCoord.

Available only in the fragment language, gl_FragCoord is an input variable that contains the window canvas relative coordinate (x, y, z, 1/w) values for the fragment.

khronos.org

If you notice, the function radialRainbow needs a variable called st as first parameter, whose values must be the pixel coordinates relative to the canvas and, like UVs, go between 0 and 1. The variable st is the result of the division of gl_FragCoord by the resolution:

/**
 * gl_FragCoord: pixel coordinates
 * u_resolution: the resolution of our canvas
 */
vec2 st = gl_FragCoord.xy / u_resolution;

The following image explains the difference between using UVs and st.

Once we’re able to render the radial gradient, let’s create the function to get the borders:

float borders(vec2 uv, float strokeWidth) {
  vec2 borderBottomLeft = smoothstep(vec2(0.0), vec2(strokeWidth), uv);

  vec2 borderTopRight = smoothstep(vec2(0.0), vec2(strokeWidth), 1.0 - uv);

  return 1.0 - borderBottomLeft.x * borderBottomLeft.y * borderTopRight.x * borderTopRight.y;
}

#pragma glslify: export(borders);

And then our final fragment shader:

precision mediump float;

uniform vec2 u_resolution;
uniform float u_tick;

varying vec2 v_uv;
varying float v_depth;

#pragma glslify: borders = require(borders.glsl);
#pragma glslify: radialRainbow = require(radial-rainbow.glsl);

void main() {
  // screen coordinates
  vec2 st = gl_FragCoord.xy / u_resolution;

  vec4 bordersColor = radialRainbow(st, u_tick);

  // opacity factor based on the z value
  float depth = clamp(smoothstep(-1.0, 1.0, v_depth), 0.6, 0.9);

  bordersColor *= vec4(borders(v_uv, 0.011)) * depth;

  gl_FragColor = bordersColor;
}

Drawing the content

Please note that the Apple logo is a trademark of Apple Inc., registered in the U.S. and other countries. We are only using it here for demonstration purposes.

Now that we have the cube, it’s time to add the Apple logo and all texts.

If you notice, the content is not only rendered inside the cube, but also on the three back faces as reflection – that means render it four times. In order to keep the performance high, we’ll draw it only once off-screen at render time to then use it in the various fragments.

In WebGL we can do it thanks to the FBO:

The frame buffer object architecture (FBO) is an extension to OpenGL for doing flexible off-screen rendering, including rendering to a texture. By capturing images that would normally be drawn to the screen, it can be used to implement a large variety of image filters, and post-processing effects.

Wikipedia

In Regl it’s pretty simple to play with FBOs:

...

// here we'll put the logo and the texts
const textures = [
  ...
]

// we create the FBO
const contentFbo = regl.framebuffer()

// animate is executed at render time
const animate = ({viewportWidth, viewportHeight}) => {
  contentFbo.resize(viewportWidth, viewportHeight)

  // we tell WebGL to render off-screen, inside the FBO
  contentFbo.use(() => {
    /**
     * – Content program
     * It'll run as many times as the textures number
     */
    content({
      textures
    })
  })

  /**
   * – Cube program
   * It'll run twice, once for the back faces and once for front faces
   * Together with front faces we'll render the content as well
   */
  cube([
    {
      pass: 1,
      cullFace: 'FRONT',
    },
    {
      pass: 2,
      cullFace: 'BACK',
      texture: contentFbo, // we pass the FBO as a normal texture
    },
  ])
}

regl.frame(animate)

And then update the cube fragment shader to render the content:

precision mediump float;

uniform vec2 u_resolution;
uniform float u_tick;
uniform int u_pass;
uniform sampler2D u_texture;

varying vec2 v_uv;
varying float v_depth;

#pragma glslify: borders = require(borders.glsl);
#pragma glslify: radialRainbow = require(radial-rainbow.glsl);

void main() {
  // screen coordinates
  vec2 st = gl_FragCoord.xy / u_resolution;

  vec4 texture;
  vec4 bordersColor = radialRainbow(st, u_tick);

  // opacity factor based on the z value
  float depth = clamp(smoothstep(-1.0, 1.0, v_depth), 0.6, 0.9);

  bordersColor *= vec4(borders(v_uv, 0.011)) * depth;

  if (u_pass == 2) {
    texture = texture2D(u_texture, st);
  }

  gl_FragColor = texture + bordersColor;
}

Masking

In the Apple animation every cube face shows a different texture, that means that we have to create a special mask that follows the cube rotation.

We’ll render the informations to mask the textures inside an FBO that we’ll pass to the content program.

To each texture let’s associate a different maskId – every ID corresponds to a color that we’ll use as test-data:

const textures = [
  {
    texture: logoTexture,
    maskId: 1,
  },
  {
    texture: logoTexture,
    maskId: 2,
  },
  {
    texture: logoTexture,
    maskId: 3,
  },
  {
    texture: text1Texture,
    maskId: 4,
  },
  {
    texture: text2Texture,
    maskId: 5,
  },
]

To make each maskId correspond to a colour, we just have to convert it in binary and then read it as RGB:

MaskID 1 => [0, 0, 1] => Blue
MaskID 2 => [0, 1, 0] => Lime
MaskID 3 => [0, 1, 1] => Cyan
MaskID 4 => [1, 0, 0] => Red
MaskID 5 => [1, 0, 1] => Magenta

The mask will be nothing but our cube with the faces filled with one of the colours shown above – obviously in this case we just need to draw the front faces:

...

maskFbo.use(() => {
  cubeMask([
    {
      cullFace: 'BACK',
      colorFaces: [
        [0, 1, 1], // front face => mask 3
        [0, 0, 1], // right face => mask 1
        [0, 1, 0], // back face => mask 2
        [0, 1, 1], // left face => mask 3
        [1, 0, 0], // top face => mask 4
        [1, 0, 1], // bottom face => mask 5
      ]
    },
  ])
});

contentFbo.use(() => {
  content({
    textures,
    mask: maskFbo
  })
})

...

Our mask will look like this:

Now that we have the mask available inside the fragment of the content program, let’s write down the test:

precision mediump float;

uniform vec2 u_resolution;
uniform sampler2D u_texture;
uniform int u_maskId;
uniform sampler2D u_mask;

varying vec2 v_uv;

void main() {
  vec2 st = gl_FragCoord.xy / u_resolution;

  vec4 texture = texture2D(u_texture, v_uv);

  vec4 mask = texture2D(u_mask, st);

  // convert the mask color from binary (rgb) to decimal
  int maskId = int(mask.r * 4.0 + mask.g * 2.0 + mask.b * 1.0);

  // if the test passes then draw the texture
  if (maskId == u_maskId) {
    gl_FragColor = texture;
  } else {
    discard;
  }
}

Distortion

The distortion at the edges is that characteristic that gives the feeling of a glass material.

The effect is achieved by simply shifting the pixels near the edges towards the center of each face – the following video shows how it works:

For each pixel to move we need two pieces of information:

  1. How much to move the pixel
  2. The direction in which we want to move the pixel

These two pieces of information are contained inside the Displacement Map which, as before for the mask, we’ll store in an FBO that we’ll pass to the content program:

...

displacementFbo.use(() => {
  cubeDisplacement([
    {
      cullFace: 'BACK'
    },
  ])
});

contentFbo.use(() => {
  content({
    textures,
    mask: maskFbo,
    displacement: displacementFbo
  })
})

...

The displacement map we’re going to draw will look like this:

Let’s see in detail how it’s made.

The green channel is the length, that is how much to move the pixel – the greener the greater the displacement. Since the distortion must be present only at the edges, we just have to draw a green frame on each face.

To get the green frame we just have to reuse the border function and put the result on the gl_FragColor green channel:

precision mediump float;

varying vec2 v_uv;

#pragma glslify: borders = require(borders.glsl);

void main() {
  // Green channel – how much to move the pixel
  float length = borders(v_uv, 0.028) + borders(v_uv, 0.06) * 0.3;

  gl_FragColor = vec4(0.0, length, 0.0, 1.0);
}

The red channel is the direction, whose value is the angle in radians. Finding this value is more tricky because we need the position of each point relative to the world – since our cube rotates, even the UVs follow it and therefore we loose any reference. In order to compute the position of every pixel in relation to the center we need two varying variables from the vertex shader:

  1. v_point: the world position of the current pixel.
  2. v_center: the world position of the center of the face.

The vertex shader:

precision mediump float;

attribute vec3 a_position;
attribute vec3 a_center;
attribute vec2 a_uv;

uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_world;

varying vec3 v_center;
varying vec3 v_point;
varying vec2 v_uv;

void main() {
  vec4 position = u_projection * u_view * u_world * vec4(a_position, 1.0);
  vec4 center = u_projection * u_view * u_world * vec4(a_center, 1.0);

  v_point = position.xyz;
  v_center = center.xyz;
  v_uv = a_uv;

  gl_Position = position;
}

At this point, in the fragment, we just have to find the distance from the center, calculate the relative angle in radians and put the result on the gl_FragColor red channel – here the shader updated:

precision mediump float;

varying vec3 v_center;
varying vec3 v_point;
varying vec2 v_uv;

const float PI2 = 6.283185307179586;

#pragma glslify: borders = require(borders.glsl);

void main() {
  // Red channel – which direction to move the pixel
  vec2 toCenter = v_center.xy - v_point.xy;
  float direction = (atan(toCenter.y, toCenter.x) / PI2) + 0.5;

  // Green channel – how much to move the pixel
  float length = borders(v_uv, 0.028) + borders(v_uv, 0.06) * 0.3;

  gl_FragColor = vec4(direction, length, 0.0, 1.0);
}

Now that we have our displacement map, let’s update the content fragment shader:

precision mediump float;

uniform vec2 u_resolution;
uniform sampler2D u_texture;
uniform int u_maskId;
uniform sampler2D u_mask;

varying vec2 v_uv;

void main() {
  vec2 st = gl_FragCoord.xy / u_resolution;

  vec4 displacement = texture2D(u_displacement, st);
  // get the direction by taking the displacement red channel and convert it in a vector2
  vec2 direction = vec2(cos(displacement.r * PI2), sin(displacement.r * PI2));
  // get the length by taking the displacement green channel
  float length = displacement.g;

  vec2 newUv = v_uv;
  
  // calculate the new uvs
  newUv.x += (length * 0.07) * direction.x;
  newUv.y += (length * 0.07) * direction.y;

  vec4 texture = texture2D(u_texture, newUv);

  vec4 mask = texture2D(u_mask, st);

  // convert the mask color from binary (rgb) to decimal
  int maskId = int(mask.r * 4.0 + mask.g * 2.0 + mask.b * 1.0);

  // if the test passes then draw the texture
  if (maskId == u_maskId) {
    gl_FragColor = texture;
  } else {
    discard;
  }
}

Reflection

Since reflection is quite a complex topic, I’ll just give you a quick introduction on how it works so that you can more easily understand the source I shared.

Before continuing, it’s necessary to understand the concept of camera in WebGL. The camera is nothing but the combination of two matrices: the view and projection matrix.

The projection matrix is used to convert world space coordinates into clip space coordinates. A commonly used projection matrix, the perspective matrix, is used to mimic the effects of a typical camera serving as the stand-in for the viewer in the 3D virtual world. The view matrix is responsible for moving the objects in the scene to simulate the position of the camera being changed.

developer.mozilla.org

 I suggest that you also get familiar with these concepts before we dig deeper:

In a 3D environment, reflections are obtained by creating a camera for each reflective surface and placing it accordingly based on the position of the viewer – that is the eye of the main camera.

In our case, every face of the cube is a reflective surface, that means we need 6 different cameras whose position depends on the viewer and the cube rotation.

WebGL Cubemaps

Every camera generates a texture for each inner face of the cube. Instead of creating a single framebuffer for every face, we can use the cube mapping technique.

Another kind of texture is a cubemap. It consists of 6 textures representing the 6 faces of a cube. Instead of the traditional texture coordinates that have 2 dimensions, a cubemap uses a normal, in other words a 3D direction. Depending on the direction the normal points one of the 6 faces of the cube is selected and then within that face the pixels are sampled to produce a color.

WebGL Fundamentals

So we just have to store what the six cameras “see” in the right cell – this is how our cubemap will look like:

Let’s update our animate function by adding the reflection:

...

// this is a normal FBO
const contentFbo = regl.framebuffer()

// this is a cube FBO, that means it composed by 6 textures
const reflectionFbo = regl.framebufferCube(1024)

// animate is executed at render time
const animate = ({viewportWidth, viewportHeight}) => {
  contentFbo.resize(viewportWidth, viewportHeight)

  contentFbo.use(() => {
    ...
  })

  /**
   * – Reflection program
   * we'll iterate 6 times over the reflectionFbo and draw inside the 
   * result of each camera
   */
  reflection({
    reflectionFbo,
    cameraConfig,
    texture: contentFbo
  })

  /**
   * – Cube program
   * with the back faces we'll render the reflection as well
   */
  cube([
    {
      pass: 1,
      cullFace: 'FRONT',
      reflection: reflectionFbo,
    },
    {
      pass: 2,
      cullFace: 'BACK',
      texture: contentFbo,
    },
  ])
}

regl.frame(animate)

And then update the cube fragment shader.

In the fragment shader we need to use a samplerCube instead of a sampler2D and use textureCube instead of texture2D. textureCube takes a vec3 direction so we pass the normalized normal. Since the normal is a varying and will be interpolated we need to normalize it.

WebGL Fundamentals
precision mediump float;

uniform vec2 u_resolution;
uniform float u_tick;
uniform int u_pass;
uniform sampler2D u_texture;
uniform samplerCube u_reflection;

varying vec2 v_uv;
varying float v_depth;
varying vec3 v_normal;

#pragma glslify: borders = require(borders.glsl);
#pragma glslify: radialRainbow = require(radial-rainbow.glsl);

void main() {
  // screen coordinates
  vec2 st = gl_FragCoord.xy / u_resolution;

  vec4 texture;
  vec4 bordersColor = radialRainbow(st, u_tick);

  // opacity factor based on the z value
  float depth = clamp(smoothstep(-1.0, 1.0, v_depth), 0.6, 0.9);

  bordersColor *= vec4(borders(v_uv, 0.011)) * depth;

  // if u_pass is 1, we're drawing back faces
  if (u_pass == 1) {
    vec3 normal = normalize(v_normal);
    texture = textureCube(u_reflection, normal);
  }

  // if u_pass is 1, we're drawing back faces
  if (u_pass == 2) {
    texture = texture2D(u_texture, st);
  }

  gl_FragColor = texture + bordersColor;
}

Conclusion

This article may give you a general idea of the techniques I used to replicate the Apple animation. If you want to learn more, I suggest you download the source and have a look ot how it works. If you have any questions, feel free to ask me on Twitter (@lorenzocadamuro); hope you have enjoyed it!

How to Create the Apple Fifth Avenue Cube in WebGL was written by Lorenzo Cadamuro and published on Codrops.