Collective #550

Original Source: http://feedproxy.google.com/~r/tympanus/~3/1e-T7jl4vnE/

C550_WOTW

Inspirational Website of the Week: Déplacé Maison

A refreshing design with lots of character and perfect details. Our pick this week.

Check it out

C550_form

Where to put buttons on forms

Button placement can make or break a form. Find out the best place to put them in this article by Adam Silver.

Read it

C550_symbols

Art of Symbols

Emotive Brand’s creative exploration on how ancient symbols inform contemporary brand design.

Check it out

C537_divi

Our Sponsor
The Ultimate WordPress Page Builder

You’ve never built a WordPress website like this before. Divi is more than just a WordPress theme, it’s a completely new website building platform that replaces the standard WordPress post editor with a vastly superior visual editor.

Try it

C550_Copypalette

CopyPalette

Create consistent monochromatic color palettes and export them to Figma or Sketch.

Check it out

C550_wave2

wAve

A wonderful wavy letter demo by Adam Kuhn.

Check it out

C550_smcover

My Design Process Of The Cover Design For Smashing Magazine Print Issue #1

In this article, Veerle Pieters, sheds some light into her design process of Smashing Magazine’s cover.

Read it

C550_hooks

Thinking in React Hooks

Amelia Wattenberger’s draft for a guide to the fundamental mindset change when switching from React class components to functional components + hooks.

Read it

C550_faces

Generated Photos

100,000 AI-generated faces free to use in any project.

Check it out

C550_monocle

Monocle

A wonderful magnifying list experiment by Hakim El Hattab.

Check it out

C550_andy

Keeping it simple with CSS that scales

A written version of Andy Bell’s talk he first delivered at “State of the Browser 2019”.

Read it

C550_MichelleBarker

Thoughts on the State of the Web

Michelle Barker reflects on “State of the Browser” conference and shares some thoughts on the current state of the web.

Read it

C550_keyframes

Using Custom Properties to Wrangle Variations in Keyframe Animations

Read how Sandrina Pereira found a way to account for variations within a keyframe animation using custom properties.

Read it

C550_darkmode

Dark Mode — Working with Color Systems

Søren Clausen writes how with the introduction of Dark Modes, colors now also need to be put into systems.

Read it

C550_fish

Upside down

A hypnotizing demo made by K-T.

Check it out

C550_raindrop

Raindrop

A rain effect that realistically interacts with elements on a page. By Neal Agarwal.

Check it out

C550_v8

Nullish coalescing

Learn about the nullish coalescing proposal that adds a new short-circuiting operator meant to handle default values in JavaScript.

Read it

C550_letteranim

Typing Animation.. with Puns!

A fun typing animation made by Dave Quah.

Check it out

C550_testwindows

Testing Accessibility on Windows with VirtualBox

The Accessibility DIY Kit for VirtualBox lets you test the accessibility of your website on Windows when working on a Mac.

Read it

C550_Krishna

Free Font: Krisha

A bold and beautiful display typeface by Daler Mukhiddinov.

Get it

C550_3dcolorcustomizer

From Our Blog
How to Build a Color Customizer App for a 3D Model with Three.js

Learn how to create a complete color customizer app for a 3d model of a chair using Three.js in this in-depth tutorial.

Read it

Collective #550 was written by Pedro Botelho and published on Codrops.

3 Best Ways To Boost The Speed Of Magento Software

Original Source: http://feedproxy.google.com/~r/Designrfix/~3/FX5F3G1yPmk/3-ways-boost-speed-magento-software

The success of your online stores depends on how fast and smooth it operates. If the site performance is not up to par with others in the industry, then you will lose a lot of potential customers. If that is the case with your online store, then you will need to monetize the Magneto software. […]

The post 3 Best Ways To Boost The Speed Of Magento Software appeared first on designrfix.com.

How to Build Your First Telegram Chatbot with Node.js

Original Source: https://www.sitepoint.com/how-to-build-your-first-telegram-chatbot-with-node-js/?utm_source=rss

So, this morning you woke up with the idea to develop a way to store and label interesting articles you’ve read. After playing with the idea, you figure a Telegram chatbot is the most convenient solution for this problem.

In this guide, we’ll walk you through everything you need to know to build your first Telegram chatbot using JavaScript and Node.js.

To get started, we have to register our new bot with the so-called Botfather to receive our API access token.

Bot Registration with @BotFather

The first step towards our very own Telegram bot is registering the bot with the BotFather. The BotFather is a bot itself that makes your life much easier. It helps you with registering bots, changing the bot description, adding commands, and providing you with the API token for your bot.

The API token is the most important step, as this allows you to run the code that can perform tasks for the bot.

1. Finding the BotFather

The BotFather can be found on Telegram by searching for ‘BotFather’. Click on the official BotFather, indicated with the white checkmark icon in the blue circle.

2. Registering a New Bot

Now we’ve found BotFather, let’s talk to him! You can start the conversation by typing /newbot. BotFather will ask you to choose a name for your both. This name can be anything and doesn’t have to be unique. To keep things simple, I named my bot ArticleBot.

Next, you will be prompted to input a username for the bot. The username must be unique and end in bot. Therefore, I chose michiel_article_bot, as that username was not yet taken. This will also be the username you use for looking up the bot in Telegram’s search field.

FatherBot will return a success message with your token to access the Telegram HTTP API. Make sure to store this token safely, and certainly don’t share it with anyone else.

3. Modifying the Bot

We can further modify the bot by adding a description or setting the commands we wish the bot to know. You can message the bot with the text /setcommands. It will show you how to input the commands with the format command1 – Description.

The post How to Build Your First Telegram Chatbot with Node.js appeared first on SitePoint.

Use Quantity Queries to Make Your CSS Quantity-Aware

Original Source: https://www.hongkiat.com/blog/quantity-queries-css-quantity-aware/

Quantity queries are specially set-up CSS selectors that allow developers to make their code quantity-aware. In responsive design, we usually use media queries to adapt our design to different…

Visit hongkiat.com for full content.

Web design turns nasty in Labour's snarky 404 page

Original Source: http://feedproxy.google.com/~r/CreativeBloq/~3/hm8C9zO9Td4/labour-404-page

It's a funny old time in British politics. If you've not been keeping up with the latest developments (and we can't blame you if you haven't), the Conservatives recently hit out at Labour leader Jeremy Corbyn with a KFC branding-themed stunt. Now it's Labour's turn to strike back, and they've done so via the medium of website error pages.

If you've already checked out our list of the best 404 pages, you'll know that they're a great way to inject a bit of humour into a website. And the Labour party has used its website's error page to make a pointed joke about the Conservative Prime Minister, Boris Johnson.

When you head over to the Labour site's 404 page, you'll see the familiar 'Oops! Page not found' message used in one form or another by plenty of online platforms. But underneath there's the catty message "Just like the Prime Minister, this page appears to be missing." Ouch.

Labour 404 page

Sssh, nobody mention Brexit

This is in reference to a recent event in Luxembourg City, where Johnson failed to take to his podium at a press conference amid booing protestors. Luxembourg's Prime Minister, Xavier Bettel, was left to speak for himself and made some pretty cutting remarks about the Conservative leader in the process.

And lo and behold, a GIF of Xavier Bettel gesturing to the empty press conference podium has been inserted on Labour's 404 page; his endlessly looping expression only adding insult to injury.

As far as political burns go, this one is brutal but classy. While the Conservative's Jeremy Corbyn KFC stunt got kind of exhausting with its heavy-handedness, this 404 page is at least useful as it helps redirect visitors to the Labour homepage and the party's manifesto.

Related articles:

Who are the unsung heroes of web design?5 inspiring web design case studiesThe hottest web design trends of 2019

How to Build Unique, Beautiful Websites with Tailwind CSS

Original Source: https://www.sitepoint.com/tailwind-unique-beautiful-websites/?utm_source=rss

Build Unique and Beautiful Web Sites with Tailwind CSS

When thinking about what CSS framework to use for a new project, options like Bootstrap and Foundation readily jump to mind. They’re tempting to use because of their ready-to-use, pre-designed components, which developers can use with ease right away. This approach works well with relatively simple websites with a common look and feel. But as soon as we start building more complex, unique sites with specific needs, a couple of problems arise.

At some point, we need to customize certain components, create new components, and make sure the final codebase is unified and easy to maintain after the changes.

It’s hard to satisfy the above needs with frameworks like Bootstrap and Foundation, which give us a bunch of opinionated and, in many cases, unwanted styles. As a result, we have to continuously solve specificity issues while trying to override the default styles. It doesn’t sound like a fun job, does it?

Ready-to-use solutions are easy to implement, but inflexible and confined to certain boundaries. On other hand, styling web sites without a CSS framework is powerful and flexible, but isn’t easy to manage and maintain. So, what’s the solution?

The solution, as always, is to follow the golden middle. We need to find and apply the right balance between the concrete and abstract. A low-level CSS framework offers such a balance. There are several frameworks of this kind, and in this tutorial, we’ll explore the most popular one, Tailwind CSS.

What Is Tailwind?

Tailwind is more than a CSS framework, it’s an engine for creating design systems. — Tailwind website

Tailwind is a collection of low-level utility classes. They can be used like lego bricks to build any kind of components. The collection covers the most important CSS properties, but it can be easily extended in a variety of ways. With Tailwind, customization isn’t pain in the neck anymore. The framework has great documentation, covering every class utility in detail and showing the ways it can be customized. All modern browsers, and IE11+, are supported.

Why Using Utility-first Framework?

A low-level, utility-first CSS framework like Tailwind has a plenty of benefits. Let’s explore the most significant of them:

You have greater control over elements’ appearance. We can change and fine-tune an element’s appearance much more easily with utility classes.
It’s easy to manage and maintain in large projects, because you only maintain HTML files, instead of a large CSS codebase.
It’s easier to build unique, custom website designs without fighting with unwanted styles.
It’s highly customizable and extensible, which gives us unlimited flexibility.
It has a mobile-first approach and easy implementation of responsive design patterns.
There’s the ability to extract common, repetitive patterns into custom, reusable components — in most cases without writing a single line of custom CSS.
It has self-explanatory classes. We can imagine how the styled element looks only by reading the classes.

Finally, as Tailwind’s creators say:

it’s just about impossible to think this is a good idea the first time you see it — you have to actually try it.

So, let’s try it!

Getting Started with Tailwind

To demonstrate Tailwind’s customization features, we need to install it via npm:

npm install tailwindcss

The next step is to create a styles.css file, where we include the framework styles using the @tailwind directive:

@tailwind base;

@tailwind components;

@tailwind utilities;

After that, we run the npx tailwind init command, which creates a minimal tailwind.config.js file, where we’ll put our customization options during the development. The generated file contains the following:

module.exports = {
theme: {},
variants: {},
plugins: [],
}

The next step is to build the styles in order to use them:

npx tailwind build styles.css -o output.css

Finally, we link the generated output.css file and Font Awesome in our HTML:

<link rel=”stylesheet” type=”text/css” href=”output.css”>
<link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css”>

And now, we’re ready to start creating.

Building a One-page Website Template

In the rest of the tutorial, we’ll build a one-page website template using the power and flexibility of Tailwind’s utility classes.

Here you can see the template in action.

I’m not going to explain every single utility (which would be boring and tiresome) so I suggest you to use the Tailwind cheatsheet as a quick reference. It contains all available utilities with their effect, plus direct links to the documentation.

We’ll build the template section by section. They are Header, Services, Projects, Team, and Footer.

We firstly wrap all section in a container:

<div class=”container mx-auto”>
<!– Put the sections here –>
</div>

Header (Logo, Navigation)

The first section — Header — will contain a logo on the left side and navigation links on the right side. Here’s how it will look:

The site header

Now, let’s explore the code behind it.

<div class=”flex justify-between items-center py-4 bg-blue-900″>
<div class=”flex-shrink-0 ml-10 cursor-pointer”>
<i class=”fas fa-drafting-compass fa-2x text-orange-500″></i>
<span class=”ml-1 text-3xl text-blue-200 font-semibold”>WebCraft</span>
</div>
<i class=”fas fa-bars fa-2x visible md:invisible mr-10 md:mr-0 text-blue-200 cursor-pointer”></i>
<ul class=”hidden md:flex overflow-x-hidden mr-10 font-semibold”>
<li class=”mr-6 p-1 border-b-2 border-orange-500″>
<a class=”text-blue-200 cursor-default” href=”#”>Home</a>
</li>
<li class=”mr-6 p-1″>
<a class=”text-white hover:text-blue-300″ href=”#”>Services</a>
</li>
<li class=”mr-6 p-1″>
<a class=”text-white hover:text-blue-300″ href=”#”>Projects</a>
</li>
<li class=”mr-6 p-1″>
<a class=”text-white hover:text-blue-300″ href=”#”>Team</a>
</li>
<li class=”mr-6 p-1″>
<a class=”text-white hover:text-blue-300″ href=”#”>About</a>
</li>
<li class=”mr-6 p-1″>
<a class=”text-white hover:text-blue-300″ href=”#”>Contacts</a>
</li>
</ul>
</div>

As you can see, the classes are pretty self-explanatory as I mentioned above. We’ll explore only the highlights.

First, we create a flex container and center its items horizontally and vertically. We also add some top and bottom padding, which Tailwind combines in a single py utility. As you may guess, there’s also a px variant for left and right. We’ll see that this type of shorthand is broadly used in many of the other utilities. As a background color, we use the darkest blue (bg-blue-900) from Tailwind’s color palette. The palette contains several colors with shades for each color distributed from 100 to 900. For example, blue-100, blue-200, blue-300, etc.

In Tailwind, we apply a color to a property by specifying the property followed by the color and the shade number. For example, text-white, bg-gray-800, border-red-500. Easy peasy.

For the logo on the left side, we use a div element, which we set not to shrink (flex-shrink-0) and move it a bit away from the edge by applying the margin-left property (ml-10). Next we use a Font Awesome icon whose classes perfectly blend with those of Tailwind. We use one of them to make the icon orange. For the textual part of the logo, we use big, light blue, semi-bolded text, with a small offset to the right.

In the middle, we add an icon that will be visible only on mobile. Here we use one of the responsive breakpoint prefixes (md). Tailwind, like Bootstrap and Foundation, follows the mobile-first approach. This means that when we use utilities without prefix (visible), they apply all the way from the smallest to the largest devices. If we want different styling for different devices, we need to use the breakpoint prefixes. So, in our case the icon will be visible on small devices, and invisible (md:invisible) on medium and beyond.

At the right side we put the nav links. We style the Home link differently, showing that it’s the active link. We also move the navigation from the edge and set it to be hidden on overflow (overflow-x-hidden). The navigation will be hidden (hidden) on mobile and set to flex (md:flex) on medium and beyond.

You can read more about responsiveness in the documentation.

Services

Let’s now create the next section, Services. Here’s how it will look:

The Services section

And here’s the code:

<div class=”w-full p-6 bg-blue-100″>
<div class=”w-48 mx-auto pt-6 border-b-2 border-orange-500 text-center text-2xl text-blue-700″>OUR SERVICES</div>
<div class=”p-2 text-center text-lg text-gray-700″>We offer the best web development solutions.</div>
<div class=”flex justify-center flex-wrap p-10″>
<div class=”relative w-48 h-64 m-5 bg-white shadow-lg”>
<div class=”flex items-center w-48 h-20 bg-orange-500″>
<i class=”fas fa-bezier-curve fa-3x mx-auto text-white”></i>
</div>
<p class=”mx-2 py-2 border-b-2 text-center text-gray-700 font-semibold uppercase”>UI Design</p>
<p class=”p-2 text-sm text-gray-700″>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac est massa.</p>
<div class=”absolute right-0 bottom-0 w-8 h-8 bg-gray-300 hover:bg-orange-300 text-center cursor-pointer”>
<i class=”fas fa-chevron-right mt-2 text-orange-500″></i>
</div>
</div>

</div>
</div>

We create a section with light blue background. Then we add an underlined title and a subtitle.

Next, we use a flex container for the services items. We use flex-wrap so the items will wrap on resize. We set the dimensions for each card and add some space and a drop shadow. Each card has a colored section with a topic icon, a title, and a description. And we also put a button with an icon in the bottom-right corner.

Here we use one of the pseudo-class variants (hover, focus, etc.). They’re used in the same way as responsive breakpoints. We use the pseudo-class prefix, followed by a colon and the property name (hover:bg-orange-300).

You can learn more about pseudo-class variants in the documentation.

For brevity, I show the code only for the first card. The other ones are similar. You have to change only the colors, icons, and titles. See the final HTML file on GitHub repo for a reference.

The post How to Build Unique, Beautiful Websites with Tailwind CSS appeared first on SitePoint.

How to Upgrade Nikon Camera Firmware

Original Source: https://www.hongkiat.com/blog/nikon-camera-upgrade-firmware/

Like any other device, your camera should stay updated with the latest firmware (built-in software that controls the functions of the camera). An updated firmware gives you access to the latest…

Visit hongkiat.com for full content.

Collective #548

Original Source: http://feedproxy.google.com/~r/tympanus/~3/usBbibNZpEs/

C548_WOTW

Inspirational Website of the Week: Design Embraced

Anthony Goodwin’s portfolio has a beautiful flowy design with lovely details. Our pick this week.

Get inspired

C537_divi

Our Sponsor
The Ultimate WordPress Page Builder

You’ve never built a WordPress website like this before. Divi is more than just a WordPress theme, it’s a completely new website building platform that replaces the standard WordPress post editor with a vastly superior visual editor.

Try it

C548_simplicity

Simplicity (II)

Bastian Allgeier on the lessons learned while working on very old projects and how the post-build-process era brought dependency hell.

Read it

C548_animateit

Animate it

A tool to create high-quality GIFs and video animations in your browser.

Check it out

C548_metronome

Metronomes in JavaScript

Monica Dinculescu’s exploration of metronomes and how to keep time in JavaScript.

Read it

C548_scrollbars

Two Browsers Walked Into a Scrollbar

Zach Leatherman studies scrollbar obtrusiveness and the design control we have with CSS.

Read it

C548_logicalcss

Logical Operations with CSS Variables

Ana Tudor explores the interesting possibilities of logical operations using CSS custom properties.

Read it

C548_caniemail

Can I email

A very useful site that offers support info on more than 50 HTML and CSS features tested across 25 email clients.

Check it out

C548_veoluz

VeoLuz

A generative art tool that lets you play with light in a way you never have before. By Jared Forsyth.

Check it out

C548_csscamera

CSS-Camera

Add depth using a 3D camera view to your web page with CSS3 3D transforms. By Mingyu Kim.

Check it out

C548_activetheory

Active Theory v5

The amazing new website by Active Theory.

Check it out

C548_nightbuild

Scheduling Netlify deploys with GitHub Actions

Thadee Trompetter shows how to use cron jobs on GitHub Actions and Netlify build hooks to perform nightly updates of a static site.

Read it

C548_savedata

Speeding up the web with the Save-Data header

Matt Hobbs experiments with the Save-Data header and shares what he learned.

Read it

C548_cables

Cables

In case you didn’t know about it: Cables is a tool for creating beautiful interactive content. With an easy to navigate interface and real time visuals, it allows for rapid prototyping and fast adjustments. Currently in public beta.

Check it out

C548_interview

Reverse interview

A list of questions to ask the company during your interview.

Check it out

C548_regextweet

Learn Regex in 4 tweets

A crash course on regex by Somdev Sangwan in just four tweets.

Check it out

C548_caniusemdn

Caniuse and MDN compatibility data collaboration

Read about how two great data sources on web compatibility come together.

Read it

C548_generativemountains

Procedural Mountains

A fantastic demo by Kyle Wetton.

Check it out

C548_fullstackai

fullstack.ai

End-to-end machine learning project showing key aspects of developing and deploying real life ML driven application.

Check it out

C548_dissolve

Dissolve

A wonderful demo of a dissolving effect shader by Colin Peyrat.

Check it out

C548_laser

Daytripper: Hide-My-Windows Laser Tripwire

Daytripper is a laser tripwire that can, upon triggering, hide all your windows, lock your computer, or execute a custom script to do whatever you want ?

Check it out

C548_webcamaudiovisualizer

From Our Blog
How to Create a Webcam Audio Visualizer with Three.js

A tutorial on how to create a Three.js powered audio visualizer that takes input from the user’s webcam.

Read it

C548_websitesroundup

From Our Blog
Inspirational Websites Roundup #8

Another set of inspirational website designs from the previous month to bring you up to date on the current design trends.

Check it out

Collective #548 was written by Pedro Botelho and published on Codrops.

How to Decrease Decision Fatigue and Increase Conversions

Original Source: https://www.webdesignerdepot.com/2019/09/how-to-decrease-decision-fatigue-and-increase-conversions/

It sort of feels like our lives are nothing more than an endless string of decision-making, doesn’t it? You go to the grocery store and half an aisle is dedicated to olive oil. Or cereal. Or yogurt. Then, you go home and your streaming services give you literally hundreds of options for horror movies. Or documentaries. Or TV shows. And it doesn’t get any better online.

Decision fatigue happens when you put your consumer in the position to choose from an over-abundance of options.

That said, decision fatigue isn’t some minor frustration caused by having too many awesome choices. It can do some major harm to your conversion rate. For instance: Hick’s Law states that with each new option you put before a user, the longer it will take them to process all of their choices. 

That’s the opposite of what you want to happen on a website. You want visitors to quickly explore, find exactly what they need, and convert. The longer you delay this, the lower your chances will be to convert them at all. 

Or, you might find that too many choices lead visitors to make poor buying decisions. 

This happens when there are too many similar-looking options or there’s an excess of information and the customer gives up. They know they need to buy something, so they make a hasty purchase just to “get it over with”. As a result, the company ends up having to deal with more returns and refunds because of unsatisfied customers. And they will cost you. 

Your website already has enough competition to contend with, so why create competition for your visitors’ attention internally?

What Web Designers Can Do to Reduce Design Fatigue

If you or your client want your website to convert, you really need to think about the ways you’re forcing them to stop and wonder: “Which one do I choose?”

Whether your website sells content, services, or products, less is always going to be more in terms of decision-making. And this isn’t about how many products you sell on a website. This is about how you frame every individual decision leading up to conversion. 

Here are some examples of where decision fatigue may take place and how to reduce friction there: 

1. Clear Up the Navigation

With smaller websites and more narrowly-focused businesses, you won’t have to worry about this too much. With big stores, however, the navigation can get you into a lot of trouble if you don’t organize it well.

Here’s what I mean: 

This is the website for ShopRite, a major grocery chain in the U.S.

While I understand that people may come to this website for a variety of purposes, it’s clearly set up as an online ordering system. If that’s your main priority, then the navigation should not be organized in this manner. 

“Shop Aisles” certainly belongs there. But do they need three menus to promote specials and coupons: “Weekly Circular”, “Online Promotions”, and “Save”? What about “Discover”, “Careers”, and “Recipes”? There’s a lot of extra noise here. 

If the data is telling you that the majority of customers come here to shop, give them what they want in a clear and simple fashion. Don’t make them read through other menu labels or click on them to figure out what something labeled “Discover” even means. 

2. Choose One Promotion

Promotional offers and discounts are a great way to entice new visitors to take the plunge and for returning visitors to buy again. But just because they can be effective in increasing sales doesn’t mean you can go overboard with it. 

For instance: 

This is what the Bed, Bath & Beyond website looks like at the time of writing. Above the fold, there are promotions for: 

“Free shipping on orders over $39”
“Reserve online and free pick-up in store”
“Up to 50% off select Shark vacuums”
“Save $100 on the Artisan Stand Mixer”
“Up to 75% off deals for every home in your room”
And, depending on where you catch the slider, there are rotating offers there as well. 

Basically, the first impression they want anyone to have is: “Never pay full price with us!” and “We have so many deals, it’s going to be impossible to decide where to get started!”

Even if the offers your website advertises don’t conflict with one another, each one still requires visitors’ attention, which is a problem. When you offer something special, they’re going to take time to read it and make sure they’re not missing out on anything. 

To keep your design from distracting visitors from converting, only include one (or maybe two) promotions on a page.

3. Reduce the Number and Variety of CTAs

This typically isn’t a problem on internal pages of a website where the structure is simpler: 

This is the topic;
This is the call-to-action.

The home page, however, can be more complicated.

This is a section from the lower part of the Thrive digital agency website:

The home page, in general, is way too long. While it’s important to demonstrate authority and leverage customer testimonials when possible, this page is overkill. Then, when visitors get through the bravado, they stumble upon the call-to-action section above where they can: 

Get a free proposal;
Call someone;
Follow them on up to five social media platforms;
Contact them through the website;
Learn about how the agency gives back.
Read more customer reviews.

If you’ve already taken up that much time to convince visitors of your trustworthiness, then it’s time to give them one clear call-to-action. It’s up to you to decide what the priority is and then let the navigation provide other options if the primary CTA isn’t a good fit.

4. Show Only the Most Recent Content

Content marketing is an essential part of most websites. And while it might seem like a good idea to show off how much high-quality content is available all at once, too many options can trigger decision fatigue. 

Just like what happens with this example from the Convince & Convert website:

In just this screenshot, you can see six pieces of lookalike content, with four more poking up from the bottom. That’s ten headlines visitors have to read to determine which one or ones is worth clicking on.

You’d be better off using a recent posts slider where only one post shows at a time. Then, the “Blog” link in the navigation or the search bar at the top of the site can help them find more content if they’re interested in exploring.

5. Include Filters and Sort Functionality for Search Results

When it comes to online search, you don’t want to take a page out of Google’s book where the responsibility is on the users to type exactly what they’re looking for and to decide how far they want to dig into the search results pages (which usually isn’t far).

Give your visitors an easy way to filter and sort their search results so that the most relevant options present themselves and everything else is weeded out.

For example, this is the Patina Restaurant Group’s chain of restaurants from around the US:

This page has dozens of dining options split up per location. It’s not a great approach to helping diners find the perfect place to eat with so much information being thrown at them.

That said, Patina Restaurant Group does have a solution for the problem. At the top of the page, there’s a custom search filter people can use to sort by location or by occasion:

In this particular example, it takes the list of 13 Midtown restaurants I was looking at before, down to 2 options.

As you look for ways to narrow down search results for your own users, don’t be afraid to look outside the box and develop filters specific to the things they’re looking for.

Wrap-Up

I know it might seem like a good idea to give your visitors as many options as they want. It almost seems luxurious, right? But when you ask visitors to make too many decisions, you run the risk of creating friction that doesn’t need to be there.

To reduce decision fatigue, take a look at your website and identify those spots where users have to make a choice.

Do I stop and watch this video… or do I request a free consultation?

Do I look at the 24 phones available… or do I do a side-by-side comparison of the top 3 top-rated phones to see which ones have the features I need?

Do I buy the mattress I came here for… or do I spend 20 minutes reviewing the specs of the one that’s 50% off this week?

Your goal is to design a website that turns visitors into customers. But, more than that, your goal is to design a website that turns visitors into satisfied customers. Make the decision-making process simple and easy so that can happen.

 

Featured image via DepositPhotos.

Source

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

How to Build a Color Customizer App for a 3D Model with Three.js

Original Source: http://feedproxy.google.com/~r/tympanus/~3/WhzsB3GOGJ0/





In this tutorial you’ll learn how to create a customizer app that lets you change the colors of a 3D model of a chair using Three.js.

3DModelCustomizer01

See the demo in action: 3D Model Color Customizer App with Three.js

A quick introduction

This tool is built inspired by the Vans shoe customizer, and uses the amazing JavaScript 3D library Three.js.

For this tutorial, I’ll assume you are comfortable with JavaScript, HTML and CSS.

I’m going to do something a little bit different here in the interest of actually teaching you, and not making you copy/paste parts that aren’t all that relevant to this tutorial, we’re going to start with all of the CSS in place. The CSS really is just for the dressing around the app, it focusses on the UI only. That being said, each time we paste some HTML, I’ll explain quickly what the CSS does. Let’s get started.

Part 1: The 3D model

If you want to skip this part entirely, feel free to do so, but it may pay to read it just so you have a deeper understanding of how everything works.

This isn’t a 3D modelling tutorial, but I will explain how the model is set up in Blender, and if you’d like to create something of your own, change a free model you found somewhere online, or instruct someone you’re commissioning. Here’s some information about how our chairs 3D model is authored.

The 3D model for this tutorial is hosted and included within the JavaScript, so don’t worry about downloading or having to do any of this unless you’d like to look further into using Blender, and learning how to create your own model.

Scale

The scale is set to approximately what it would be in the real world; I don’t know if this is important, but it feels like the right thing to do, so why not?

blender-a

Layering and naming conventions

This part is important: each element of the object you want to customize independently needs to be its own object in the 3D scene, and each item needs to have a unique name. Here we have back, base, cushions, legs and supports. Note that if you have say, three items all called supports, Blender is going to name them as supports, supports.001, supports.002. That doesn’t matter, because in our JavaScript we’ll be using includes(“supports”) to find all of those objects that contain the string supports in it.

blender-b

Placement

The model should be placed at the world origin, ideally with its feet on the floor. It should ideally be facing the right way, but this can easily be rotated via JavaScript, no harm, no foul.

Setting up for export

Before exporting, you want to use Blender’s Smart UV unwrap option. Without going too much into detail, this makes textures keep its aspect ratio in tact as it wraps around the different shapes in your model without stretching in weird ways (I’d advise reading up on this option only if you’re making your own model).

You want to be sure to select all of your objects, and apply your transformations. For instance, if you changed the scale or transformed it in any way, you’re telling Blender that this is the new 100% scale, instead of it still being 32.445% scale if you scaled it down a bit.

File Format

Apparently Three.js supports a bunch of 3D object file formats, but the one it recommends is glTF (.glb). Blender supports this format as an export option, so no worries there.

Part 2: Setting up our environment

Go ahead and fork this pen, or start your own one and copy the CSS from this pen. This is a blank pen with just the CSS we’re going to be using in this tutorial.

See the Pen
3D Chair Customizer Tutorial – Blank by Kyle Wetton (@kylewetton)
on CodePen.

If you don’t choose to fork this, grab the HTML as well; it has the responsive meta tags and Google fonts included.

We’re going to use three dependencies for this tutorial. I’ve included comments above each that describe what they do. Copy these into your HTML, right at the bottom:

<!– The main Three.js file –>
<script src=’https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js’></script>

<!– This brings in the ability to load custom 3D objects in the .gltf file format. Blender allows the ability to export to this format out the box –>
<script src=’https://cdn.jsdelivr.net/gh/mrdoob/Three.js@r92/examples/js/loaders/GLTFLoader.js’></script>

<!– This is a simple to use extension for Three.js that activates all the rotating, dragging and zooming controls we need for both mouse and touch, there isn’t a clear CDN for this that I can find –>
<script src=’https://threejs.org/examples/js/controls/OrbitControls.js’></script>

Let’s include the canvas element. The entire 3D experience gets rendered into this element, all other HTML will be UI around this. Place the canvas at the bottom of your HTML, above your dependencies.

<!– The canvas element is used to draw the 3D scene –>
<canvas id=”c”></canvas>

Now, we’re going to create a new Scene for Three.js. In your JavaScript, lets make a reference to this scene like so:

// Init the scene
const scene = new THREE.Scene();

Below this, we’re going to reference our canvas element

const canvas = document.querySelector(‘#c’);

Three.js requires a few things to run, and we will get to all of them. The first was scene, the second is a renderer. Let’s add this below our canvas reference. This creates a new WebGLRenderer, we’re passing our canvas to it, and we’ve opted in for antialiasing, this creates smoother edges around our 3D model.

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

And now we’re going to append the renderer to the document body

document.body.appendChild(renderer.domElement);

The CSS for the canvas element is just stretching it to 100% height and width of the body, so your entire page has now turned black, because the entire canvas is now black!

Our scene is black, we’re on the right track here.

The next thing Three.js needs is an update loop, basically this is a function that runs on each frame draw and is really important to the way our app will work. We’ve called our update function animate(). Let’s add it below everything else in our JavaScript.

function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

animate();

Note that we’re referencing a camera here, but we haven’t set one up yet. Let’s add one now.

At the top of your JavaScript, we’ll add a variable called cameraFar. When we add our camera to our scene, it’s going to be added at position 0,0,0. Which is where our chair is sitting! so cameraFar is the variable that tells our camera how far off this mark to move, so that we can see our chair.

var cameraFar = 5;

Now, above our function animate() {….} lets add a camera.

// Add a camera
var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;

This is a perspective camera, with the field of view of 50, the size of the whole window/canvas, and some default clipping planes. The planes determine how near or far the camera should be before the object isn’t rendered. It’s not something we need to pay attention to in our app.

Our scene is still black, let’s set a background color.

At the top, above our scene reference, add a background color variable called BACKGROUND_COLOR.

const BACKGROUND_COLOR = 0xf1f1f1;

Notice how we used 0x instead of # in our hex? These are hexadecimal numbers, and the only thing you need to remember about that is that its not a string the way you’d handle a standard #hex variable in JavaScript. It’s an integer and it starts with 0x.

Below our scene reference, let’s update the scenes background color, and add some fog of the same color off in the distance, this is going to help hide the edges of the floor once we add that in.

const BACKGROUND_COLOR = 0xf1f1f1;

// Init the scene
const scene = new THREE.Scene();

// Set background
scene.background = new THREE.Color(BACKGROUND_COLOR );
scene.fog = new THREE.Fog(BACKGROUND_COLOR, 20, 100);

Now it’s an empty world. It’s hard to tell that though, because there’s nothing in there, nothing casting shadows. We have a blank scene. Now it’s time to load in our model.

Part 3: Loading the model

We’re going to add the function that loads in models, this is provided by our second dependency we added in our HTML.

Before we do that though, let’s reference the model, we’ll be using this variable quite a bit. Add this at the top of your JavaScript, above your BACKGROUND_COLOR. Let’s also add a path to the model. I’ve hosted it for us, it’s about 1Mb in size.

var theModel;
const MODEL_PATH = “https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/chair.glb”;

Now we can create a new loader, and use the load method. This sets theModel as our 3D models entire scene. We’re also going to set the size for this app, the right size seems to be about twice as big as it’s loaded. Thirdly, we’re going to offset the y position by -1 to bring it down a little bit, and finally we’re going to add the model to the scene.

The first parameter is the model’s filepath, the second is a function that runs once the resource is loaded, the third is undefined for now but can be used for a second function that runs while the resource is loading, and the final parameter handles errors.

Add this below our camera.

// Init the object loader
var loader = new THREE.GLTFLoader();

loader.load(MODEL_PATH, function(gltf) {
theModel = gltf.scene;

// Set the models initial scale
theModel.scale.set(2,2,2);

// Offset the y position a bit
theModel.position.y = -1;

// Add the model to the scene
scene.add(theModel);

}, undefined, function(error) {
console.error(error)
});

At this point you should be seeing a stretched, black, pixelated chair. As awful as it looks, this is right so far. So don’t worry!

model-loaded

Along with a camera, we need lights. The background isn’t affected by lights, but if we added a floor right now, it would also be black (dark). There are a number of lights available for Three.js, and a number of options to tweak all of them. We’re going to add two: a hemisphere light, and a directional light. The settings are also sorted for our app, and they include position and intensity. This is something to play around with if you ever adopt these methods in your own app, but for now, lets use the ones I’ve included. Add these lights below your loader.

// Add lights
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0xffffff, 0.61 );
hemiLight.position.set( 0, 50, 0 );
// Add hemisphere light to scene
scene.add( hemiLight );

var dirLight = new THREE.DirectionalLight( 0xffffff, 0.54 );
dirLight.position.set( -8, 12, 8 );
dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene
scene.add( dirLight );

Your chair looks marginally better! Before we continue, here’s our JavaScript so far:

var cameraFar = 5;
var theModel;

const MODEL_PATH = “https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/chair.glb”;

const BACKGROUND_COLOR = 0xf1f1f1;
// Init the scene
const scene = new THREE.Scene();
// Set background
scene.background = new THREE.Color(BACKGROUND_COLOR );
scene.fog = new THREE.Fog(BACKGROUND_COLOR, 20, 100);

const canvas = document.querySelector(‘#c’);

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

document.body.appendChild(renderer.domElement);

// Add a camerra
var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;

// Init the object loader
var loader = new THREE.GLTFLoader();

loader.load(MODEL_PATH, function(gltf) {
theModel = gltf.scene;

// Set the models initial scale
theModel.scale.set(2,2,2);

// Offset the y position a bit
theModel.position.y = -1;

// Add the model to the scene
scene.add(theModel);

}, undefined, function(error) {
console.error(error)
});

// Add lights
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0xffffff, 0.61 );
hemiLight.position.set( 0, 50, 0 );
// Add hemisphere light to scene
scene.add( hemiLight );

var dirLight = new THREE.DirectionalLight( 0xffffff, 0.54 );
dirLight.position.set( -8, 12, 8 );
dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene
scene.add( dirLight );

function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

animate();

Here’s what we should be looking at right now:

with-lights

Let’s fix the pixelation and the stretching. Three.js needs to update the canvas size when it shifts, and it needs to set its internal resolution not only to the dimensions of the canvas, but also the device pixel ratio of the screen (which is much higher on phones).

Lets head to the bottom of our JavaScript, below where we call animate(), and add this function. This function basically listens to both, the canvas size and the window size, and returns a boolean depending on whether the two sizes are the same or not. We will use that function inside the animate function to determine whether to re-render the scene. This function is also going to take into account the device pixel ratio to be sure that the canvas is sharp on mobile phones too.

Add this function at the bottom of your JavaScript.

function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
var width = window.innerWidth;
var height = window.innerHeight;
var canvasPixelWidth = canvas.width / window.devicePixelRatio;
var canvasPixelHeight = canvas.height / window.devicePixelRatio;

const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
if (needResize) {

renderer.setSize(width, height, false);
}
return needResize;
}

Now update your animate function to look like this:

function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);

if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
}

Instantly, our chair is looking so much better!

Screen Shot 2019-09-16 at 6.49.13 PM

I need to mention a couple things before we continue:

The chair is backwards, this is my bad. We’re going to simply rotate the model on its Y position
The supports are black? but the rest is white? This is because the model has some material information that has been imported with it that I had set up in Blender. This doesn’t matter, because we’re going to add a function that lets us define textures in our app, and add them to different areas of the chair when the model loads. So, if you have a wood texture and a denim texture (spoiler: we will), we will have the ability to set these on load without the user having to choose them. So the materials on the chair right now don’t matter all that much.

Humour me quickly, head to the loader function, and remember where we set the scale to (2,2,2)? Lets add this under it:

// Set the models initial scale
theModel.scale.set(2,2,2);

theModel.rotation.y = Math.PI;

Yeah, much better, sorry about that. One more thing: Three.js doesn’t have support for degrees as far as I know (?), everyone appears to be using Math.PI. This equals 180 degrees, so if you want something angled at a 45 degree angle, you’d use Math.PI / 4.

rotated

Okay, we’re getting there! We need a floor though, without a floor there can’t really be any shadows right?

Add a floor, what we’re doing here is creating a new plane (a two-dimensional shape, or a three-dimensional shape with no height).

Add this below our lights…

// Floor
var floorGeometry = new THREE.PlaneGeometry(5000, 5000, 1, 1);
var floorMaterial = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 0
});

var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -0.5 * Math.PI;
floor.receiveShadow = true;
floor.position.y = -1;
scene.add(floor);

Let’s take a look at whats happening here.

First, we made a geometry, we won’t be needing to make another geometry in Three.js in this tutorial, but you can make all sorts.

Secondly, notice how we also made a new MeshPhongMaterial and set a couple options. It’s color, and it’s shininess. Check out some of Three.js other materials later on. Phong is great because you can adjust its reflectiveness and specular highlights. There is also MeshStandardMaterial which has support for more advanced texture aspects such as metallic and ambient occlusion, and there is also the MeshBasicMaterial, which doesn’t support shadows. We will just be creating Phong materials in this tutorial.

We created a variable called floor and merged the geometry and material into a Mesh.

We set the floor’s rotation to be flat, opted in for the ability to receive shadows, moved it down the same way we moved the chair down, and then added it to the scene.

We should now be looking at this:

Screen Shot 2019-09-16 at 7.08.46 PM

We will leave it red for now, but, where are the shadows? There’s a couple of things we need to do for that. First, under our const renderer, lets include a couple of options:

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);

We’ve set the pixel ratio to whatever the device’s pixel ratio is, not relevant to shadows, but while we’re there, let’s do that. We’ve also enabled shadowMap, but there are still no shadows? That’s because the materials we have on our chair are the ones brought in from Blender, and we want to author some of them in our app.

Our loader function includes the ability to traverse the 3D model. So, head to our loader function and add this in below the theModel = gltf.scene; line. For each object in our 3D model (legs, cushions, etc), we’re going to to enable to option to cast shadows, and to receive shadows. This traverse method will be used again later on.

Add this line below theModel = gltf.scene;

theModel.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});

It looks arguably worse than it did before, but at least theres a shadow on the floor! This is because our model still has materials brought in from Blender. We’re going to replace all of these materials with a basic, white PhongMaterial.

Lets create another PhongMaterial and add it above our loader function:

// Initial material
const INITIAL_MTL = new THREE.MeshPhongMaterial( { color: 0xf1f1f1, shininess: 10 } );

This is a great starting material, it’s a slight off-white, and it’s only a little bit shiny. Cool!

We could just add this to our chair and be done with it, but some objects may need a specific color or texture on load, and we can’t just blanket the whole thing with the same base color, the way we’re going to do this is to add this array of objects under our initial material.

// Initial material
const INITIAL_MTL = new THREE.MeshPhongMaterial( { color: 0xf1f1f1, shininess: 10 } );

const INITIAL_MAP = [
{childID: “back”, mtl: INITIAL_MTL},
{childID: “base”, mtl: INITIAL_MTL},
{childID: “cushions”, mtl: INITIAL_MTL},
{childID: “legs”, mtl: INITIAL_MTL},
{childID: “supports”, mtl: INITIAL_MTL},
];

We’re going to traverse through our 3D model again and use the childID to find different parts of the chair, and apply the material to it (set in the mtl property). These childID’s match the names we gave each object in Blender, if you read that section, consider yourself informed!

Below our loader function, let’s add a function that takes the the model, the part of the object (type), and the material, and sets the material. We’re also going to add a new property to this part called nameID so that we can reference it later.

// Function – Add the textures to the models
function initColor(parent, type, mtl) {
parent.traverse((o) => {
if (o.isMesh) {
if (o.name.includes(type)) {
o.material = mtl;
o.nameID = type; // Set a new property to identify this object
}
}
});
}

Now, inside our loader function, just before we add our model to the scene (scene.add(theModel);)

Let’s run that function for each object in our INITIAL_MAP array:

// Set initial textures
for (let object of INITIAL_MAP) {
initColor(theModel, object.childID, object.mtl);
}

Finally, head back to our floor, and change the color from red (0xff0000) to a light grey(0xeeeeee).

// Floor
var floorGeometry = new THREE.PlaneGeometry(5000, 5000, 1, 1);
var floorMaterial = new THREE.MeshPhongMaterial({
color: 0xeeeeee, // <——- Here
shininess: 0
});

It’s worth mentioning here that 0xeeeeee is different to our background color. I manually dialed this in until the floor with the lights shining on it matched the lighter background color. We’re now looking at this:

See the Pen
3D Chair Customizer Tutorial – Part 1 by Kyle Wetton (@kylewetton)
on CodePen.

Congratulations, we’ve got this far! If you got stuck anywhere, fork this pen or investigate it until you find the issue.


Part 4: Adding controls

For real though this is a very small part, and is super easy thanks to our third dependency OrbitControls.js.

Above our animate function, we add this in our controls:

// Add controls
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 3;
controls.enableDamping = true;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false; // Toggle this if you’d like the chair to automatically rotate
controls.autoRotateSpeed = 0.2; // 30

Inside the animate function, at the top, add:

controls.update();

So our controls variable is a new OrbitControls class. We’ve set a few options that you can change here if you’d like. These include the range in which the user is allowed to rotate around the chair (above and below). We’ve disabled panning to keep the chair centered, enabled dampening to give it weight, and included auto rotate ability if you choose to use them. This is currently set to false.

Try click and drag your chair, you should be able to explore the model with full mouse and touch functionality!

See the Pen
Scrollable by Kyle Wetton (@kylewetton)
on CodePen.

Part 5: Changing colors

Our app currently doesn’t do anything, so this next part will focus on changing our colors. We’re going to add a bit more HTML. Afterwards, I’ll explain a bit about what the CSS is doing.

Add this below your canvas element:

<div class=”controls”>
<!– This tray will be filled with colors via JS, and the ability to slide this panel will be added in with a lightweight slider script (no dependency used for this) –>
<div id=”js-tray” class=”tray”>
<div id=”js-tray-slide” class=”tray__slide”></div>
</div>
</div>

Basically, the .controls DIV is stuck to the bottom of the screen, the .tray is set to be 100% width of the body, but its child, .tray__slide is going to fill with swatches and can be as wide as it needs. We’re going to add the ability to slide this child to explore colors as one of the final steps of this tutorial.

Let’s start by adding in a couple colors. At the top of our JavaScript, lets add an array of five objects, each with a color property.

const colors = [
{
color: ‘66533C’
},
{
color: ‘173A2F’
},
{
color: ‘153944’
},
{
color: ‘27548D’
},
{
color: ‘438AAC’
}
]

Note that these neither have # or 0x to represent the hex. We will use these colors for both in functions. Also, it’s an object because we will be able to add other properties to this color, like shininess, or even a texture image (spoiler: we will, and we will).

Lets make swatches out of these colors!

First, let’s reference our tray slider at the top of our JavaScript:

const TRAY = document.getElementById(‘js-tray-slide’);

Right at the bottom of our JavaScript, lets add a new function called buildColors and immediately call it.

// Function – Build Colors
function buildColors(colors) {
for (let [i, color] of colors.entries()) {
let swatch = document.createElement(‘div’);
swatch.classList.add(‘tray__swatch’);

swatch.style.background = “#” + color.color;

swatch.setAttribute(‘data-key’, i);
TRAY.append(swatch);
}
}

buildColors(colors);

swatches

We’re now creating swatches out of our colors array! Note that we set the data-key attribute to the swatch, we’re going to use this to look up our color and make them into materials.

Below our new buildColors function, let’s add an event handler to our swatches:

// Swatches
const swatches = document.querySelectorAll(“.tray__swatch”);

for (const swatch of swatches) {
swatch.addEventListener(‘click’, selectSwatch);
}

Our click handler calls a function called selectSwatch. This function is going to build a new PhongMaterial out of the color and call another function to traverse through our 3d model, find the part it’s meant to change, and update it!

Below the event handlers we just added, add the selectSwatch function:

function selectSwatch(e) {
let color = colors[parseInt(e.target.dataset.key)];
let new_mtl;

new_mtl = new THREE.MeshPhongMaterial({
color: parseInt(‘0x’ + color.color),
shininess: color.shininess ? color.shininess : 10

});

setMaterial(theModel, ‘legs’, new_mtl);
}

This function looks up our color by its data-key attribute, and creates a new material out of it.

This won’t work yet, we need to add the setMaterial function, (see the final line of the function we just added).

Take note of this line: setMaterial(theModel, ‘legs’, new_mtl);. Currently we’re just passing ‘legs’ to this function, soon we will add the ability to change out the different sections we want to update. But first, lets add the zcode>setMaterial

function.

Below this function, add the setMaterial function:

function setMaterial(parent, type, mtl) {
parent.traverse((o) => {
if (o.isMesh && o.nameID != null) {
if (o.nameID == type) {
o.material = mtl;
}
}
});
}

This function is similar to our initColor function, but with a few differences. It checks for the nameID we added in the initColor, and if its the same as the parameter type, it adds the material to it.

Our swatches can now create a new material, and change the color of the legs, give it a go! Here’s everything we have so far in a pen. Investigate it if you’re lost.

See the Pen
Swatches change the legs color! by Kyle Wetton (@kylewetton)
on CodePen.

Part 6: Selecting the parts to change

We can now change the color of the legs, which is awesome, but let’s add the ability to select the part our swatch should add its material to. Include this HTML just below the opening body tag, I’ll explain the CSS below.

<!– These toggle the the different parts of the chair that can be edited, note data-option is the key that links to the name of the part in the 3D file –>
<div class=”options”>
<div class=”option –is-active” data-option=”legs”>
<img src=”https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/legs.svg” alt=””/>
</div>
<div class=”option” data-option=”cushions”>
<img src=”https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/cushions.svg” alt=””/>
</div>
<div class=”option” data-option=”base”>
<img src=”https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/base.svg” alt=””/>
</div>
<div class=”option” data-option=”supports”>
<img src=”https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/supports.svg” alt=””/>
</div>
<div class=”option” data-option=”back”>
<img src=”https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/back.svg” alt=””/>
</div>
</div>

This is just a collection of buttons with custom icons in each. The .options DIV is stuck to the side of the screen via CSS (and shifts a bit with media queries). Each .option DIV is just a white square, that has a red border on it when a –is-active class is added to it. It also includes a data-option attribute that matches our nameID, so we can identify it. Lastly, the image element has a CSS property called pointer-events: none so that the event stays on the parent even if you click the image.

Options set

Lets add another variable at the top of our JavaScript called activeOptions and by default let’s set it to ‘legs’:

var activeOption = ‘legs’;

Now head back to our selectSwatch function and update that hard-coded ‘legs’ parameter to activeOption

setMaterial(theModel, activeOption, new_mtl);

Now all we need to do is create a event handler to change out activeOption when an option is clicked!

Let’s add this above our const swatches and selectSwatch function.

// Select Option
const options = document.querySelectorAll(“.option”);

for (const option of options) {
option.addEventListener(‘click’,selectOption);
}

function selectOption(e) {
let option = e.target;
activeOption = e.target.dataset.option;
for (const otherOption of options) {
otherOption.classList.remove(‘–is-active’);
}
option.classList.add(‘–is-active’);
}

We’ve added the selectOption function, which sets the activeOption to our event targets data-option value, and toggles the –is-active class. Thats it!

Try it out

See the Pen
Changing options by Kyle Wetton (@kylewetton)
on CodePen.

But why stop here? An object could look like anything, it can’t all be the same material. A chair with no wood or fabric? Lets expand our color selection a little bit. Update your color array to this:

const colors = [
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/wood.jpg’,
size: [2,2,2],
shininess: 60
},
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/denim.jpg’,
size: [3, 3, 3],
shininess: 0
},
{
color: ‘66533C’
},
{
color: ‘173A2F’
},
{
color: ‘153944’
},
{
color: ‘27548D’
},
{
color: ‘438AAC’
}
]

The top two are now textures. We’ve got wood and denim. We also have two new properties, size and shininess. Size is how often to repeat a pattern, so the larger the number, the more dense the pattern is, or more simply put – the more it repeats.

There are two function we need to update to add this ability. Firstly, lets head to the buildColors function and update to this

// Function – Build Colors

function buildColors(colors) {
for (let [i, color] of colors.entries()) {
let swatch = document.createElement(‘div’);
swatch.classList.add(‘tray__swatch’);

if (color.texture)
{
swatch.style.backgroundImage = “url(” + color.texture + “)”;
} else
{
swatch.style.background = “#” + color.color;
}

swatch.setAttribute(‘data-key’, i);
TRAY.append(swatch);
}
}

Now its checking to see if its a texture, if it is, it’s going to set the swatches background to be that texture, neat!

Screen Shot 2019-09-16 at 9.28.44 PMNotice the gap between the 5th and 6th swatch? The final batch of colors, which I will provide, is grouped into color schemes of 5 colors per scheme. So each scheme will have that small divider in it, this is set in the CSS and will make more sense in the final product.

The second function we’re going to update is the selectSwatch function. Update it to this:

function selectSwatch(e) {
let color = colors[parseInt(e.target.dataset.key)];
let new_mtl;

if (color.texture) {

let txt = new THREE.TextureLoader().load(color.texture);

txt.repeat.set( color.size[0], color.size[1], color.size[2]);
txt.wrapS = THREE.RepeatWrapping;
txt.wrapT = THREE.RepeatWrapping;

new_mtl = new THREE.MeshPhongMaterial( {
map: txt,
shininess: color.shininess ? color.shininess : 10
});
}
else
{
new_mtl = new THREE.MeshPhongMaterial({
color: parseInt(‘0x’ + color.color),
shininess: color.shininess ? color.shininess : 10

});
}

setMaterial(theModel, activeOption, new_mtl);
}

To explain what’s going on here, this function will now check if it’s a texture. If it is, it’s going to create a new texture using the Three.js TextureLoader method, it’s going to set the texture repeat using our size values, and set the wrapping of it (this wrapping option seems to work best, I’ve tried the others, so lets go with it), then its going to set the PhongMaterials map property to the texture, and finally use the shininess value.

If it’s not a texture, it uses our older method. Note that you can set a shininess property to any of our original colors!

Screen Shot 2019-09-16 at 9.50.02 PM

Important: if your textures just remain black when you try add them. Check your console. Are you getting cross domain CORS errors? This is a CodePen bug and I’ve done my best to try fix it. These assets are hosted directly in CodePen via a Pro feature so its unfortunate to have to battle with this. Apparently, the best bet here is to not visit those image URLs directly, otherwise I recommend signing up to Cloudinary and using their free tier, you may have better luck pointing your textures there.

Here’s a pen with the textures working on my end at least:

See the Pen
Texture support by Kyle Wetton (@kylewetton)
on CodePen.

Part 7: Finishing touches

I’ve had projects get run passed clients with a big button that is begging to be pressed, positively glistening with temptation to even just hover over it, and them and their co-workers (Dave from accounts) come back with feedback about how they didn’t know there was anything to be pressed (screw you, Dave).

So let’s add some calls to action. First, let’s chuck in a patch of HTML above the canvas element:

<!– Just a quick notice to the user that it can be interacted with –>
<span class=”drag-notice” id=”js-drag-notice”>Drag to rotate 360°</span>

The CSS places this call-to-action above the chair, it’s a nice big button that instructs the user to drag to rotate the chair. It just stays there though? We will get to that.

Let’s spin the chair once it’s loaded first, then, once the spin is done, let’s hide that call-to-action.

First, lets add a loaded variable to the top of our JavaScript and set it to false:

var loaded = false;

Right at the bottom of your JavaScript, add this function

// Function – Opening rotate
let initRotate = 0;

function initialRotation() {
initRotate++;
if (initRotate <= 120) {
theModel.rotation.y += Math.PI / 60;
} else {
loaded = true;
}
}

This simply rotates the the model 360 degrees within the span of 120 frames (around 2 seconds at 60fps), and we’re going to run this in the animate function to call it for 120 frames, once its done, it’s going to set loaded to true in our animate function. Here’s how it will look in its entirely with the new code at the end there:

function animate() {

controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);

if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}

if (theModel != null && loaded == false) {
initialRotation();
}
}

animate();

We check that theModel doesn’t equal null, and that the variable loaded is false, and we run that function for 120 frames, at which point the function switches to loaded = true, and our animate function ignores it.

You should have a nice spinning chair. When that chair stops is a great time to remove our call-to-action.

In the CSS, there’s a class that can be added to that call-to-action that will hide it with an animation, this animation has a delay of 3 seconds, so let’s add that class at the same time the rotation starts.

At the top of your JavaScript we will reference it:

const DRAG_NOTICE = document.getElementById(‘js-drag-notice’);

and update your animate function like so

if (theModel != null && loaded == false) {
initialRotation();
DRAG_NOTICE.classList.add(‘start’);
}

Great! Okay, here’s some more colors, update your color array, I’ve give a lightweight sliding function below it:

const colors = [
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/wood_.jpg’,
size: [2,2,2],
shininess: 60
},
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/fabric_.jpg’,
size: [4, 4, 4],
shininess: 0
},
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/pattern_.jpg’,
size: [8, 8, 8],
shininess: 10
},
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/denim_.jpg’,
size: [3, 3, 3],
shininess: 0
},
{
texture: ‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/quilt_.jpg’,
size: [6, 6, 6],
shininess: 0
},
{
color: ‘131417’
},
{
color: ‘374047’
},
{
color: ‘5f6e78’
},
{
color: ‘7f8a93′
},
{
color: ’97a1a7’
},
{
color: ‘acb4b9’
},
{
color: ‘DF9998’,
},
{
color: ‘7C6862’
},
{
color: ‘A3AB84’
},
{
color: ‘D6CCB1’
},
{
color: ‘F8D5C4’
},
{
color: ‘A3AE99’
},
{
color: ‘EFF2F2’
},
{
color: ‘B0C5C1’
},
{
color: ‘8B8C8C’
},
{
color: ‘565F59’
},
{
color: ‘CB304A’
},
{
color: ‘FED7C8’
},
{
color: ‘C7BDBD’
},
{
color: ‘3DCBBE’
},
{
color: ‘264B4F’
},
{
color: ‘389389’
},
{
color: ’85BEAE’
},
{
color: ‘F2DABA’
},
{
color: ‘F2A97F’
},
{
color: ‘D85F52’
},
{
color: ‘D92E37’
},
{
color: ‘FC9736’
},
{
color: ‘F7BD69’
},
{
color: ‘A4D09C’
},
{
color: ‘4C8A67’
},
{
color: ‘25608A’
},
{
color: ’75C8C6′
},
{
color: ‘F5E4B7’
},
{
color: ‘E69041’
},
{
color: ‘E56013’
},
{
color: ‘11101D’
},
{
color: ‘630609’
},
{
color: ‘C9240E’
},
{
color: ‘EC4B17’
},
{
color: ‘281A1C’
},
{
color: ‘4F556F’
},
{
color: ‘64739B’
},
{
color: ‘CDBAC7’
},
{
color: ‘946F43’
},
{
color: ‘66533C’
},
{
color: ‘173A2F’
},
{
color: ‘153944’
},
{
color: ‘27548D’
},
{
color: ‘438AAC’
}
]

Awesome! These hang off the page though, right at the bottom of your JavaScript, add this function, it will allow you to drag the swatches panel with mouse and touch. For the interest of keeping on topic, I won’t delve too much into how it works.

var slider = document.getElementById(‘js-tray’), sliderItems = document.getElementById(‘js-tray-slide’), difference;

function slide(wrapper, items) {
var posX1 = 0,
posX2 = 0,
posInitial,
threshold = 20,
posFinal,
slides = items.getElementsByClassName(‘tray__swatch’);

// Mouse events
items.onmousedown = dragStart;

// Touch events
items.addEventListener(‘touchstart’, dragStart);
items.addEventListener(‘touchend’, dragEnd);
items.addEventListener(‘touchmove’, dragAction);

function dragStart (e) {
e = e || window.event;
posInitial = items.offsetLeft;
difference = sliderItems.offsetWidth – slider.offsetWidth;
difference = difference * -1;

if (e.type == ‘touchstart’) {
posX1 = e.touches[0].clientX;
} else {
posX1 = e.clientX;
document.onmouseup = dragEnd;
document.onmousemove = dragAction;
}
}

function dragAction (e) {
e = e || window.event;

if (e.type == ‘touchmove’) {
posX2 = posX1 – e.touches[0].clientX;
posX1 = e.touches[0].clientX;
} else {
posX2 = posX1 – e.clientX;
posX1 = e.clientX;
}

if (items.offsetLeft – posX2 <= 0 && items.offsetLeft – posX2 >= difference) {
items.style.left = (items.offsetLeft – posX2) + “px”;
}
}

function dragEnd (e) {
posFinal = items.offsetLeft;
if (posFinal – posInitial < -threshold) { } else if (posFinal – posInitial > threshold) {

} else {
items.style.left = (posInitial) + “px”;
}

document.onmouseup = null;
document.onmousemove = null;
}

}

slide(slider, sliderItems);

Now, head to your CSS and under .tray__slider, uncomment this small animation

/* transform: translateX(-50%);
animation: wheelin 1s 2s ease-in-out forwards; */

Okay, let’s finish it off with a the final two touches, and we’re done!

Let’s update our .controls div to include this extra call-to-action:

<div class=”controls”>
<div class=”info”>
<div class=”info__message”>
<p><strong>&nbsp;Grab&nbsp;</strong> to rotate chair. <strong>&nbsp;Scroll&nbsp;</strong> to zoom. <strong>&nbsp;Drag&nbsp;</strong> swatches to view more.</p>
</div>
</div>

<!– This tray will be filled with colors via JS, and the ability to slide this panel will be added in with a lightweight slider script (no dependency used for this) –>
<div id=”js-tray” class=”tray”>
<div id=”js-tray-slide” class=”tray__slide”></div>
</div>
</div>

Note that we have a new info section that includes some instructions on how to control the app.

Finally, let’s add a loading overlay so that our app is clean while everything loads, and we will remove it once the model is loaded.

Add this to the top of our HTML, below the body tag.

<!– The loading element overlays all else until the model is loaded, at which point we remove this element from the DOM –>
<div class=”loading” id=”js-loader”><div class=”loader”></div></div>

Here’s the thing about our loader, in order for it to load first, we’re going to add the CSS to the head tag instead of being included in the CSS. So simply add this CSS just above the closing head tag.

<style>
.loading {
position: fixed;
z-index: 50;
width: 100%;
height: 100%;
top: 0; left: 0;
background: #f1f1f1;
display: flex;
justify-content: center;
align-items: center;
}

.loader{
-webkit-perspective: 120px;
-moz-perspective: 120px;
-ms-perspective: 120px;
perspective: 120px;
width: 100px;
height: 100px;
}

.loader:before{
content: “”;
position: absolute;
left: 25px;
top: 25px;
width: 50px;
height: 50px;
background-color: #ff0000;
animation: flip 1s infinite;
}

@keyframes flip {
0% {
transform: rotate(0);
}

50% {
transform: rotateY(180deg);
}

100% {
transform: rotateY(180deg) rotateX(180deg);
}
}
</style>

Almost there! Let’s remove it once the model is loaded.

At the top of our JavaScript, lets reference it:

const LOADER = document.getElementById(‘js-loader’);

Then in our loader function, after scene.add(theModel), include this line

// Remove the loader
LOADER.remove();

Now our app loads behind this DIV, polishing it off:

Screen Shot 2019-09-16 at 10.31.25 PM

And that’s it! Here’s the completed pen for reference.

See the Pen
3D Chair Customizer Tutorial – Part 4 by Kyle Wetton (@kylewetton)
on CodePen.

You can also check out the demo hosted here on Codrops.

Thank you for sticking with me!

This is a big tutorial. If you feel I made a mistake somewhere, please let me know in the comments, and thanks again for following with me as we create this absolute unit.

How to Build a Color Customizer App for a 3D Model with Three.js was written by Kyle Wetton and published on Codrops.