Collective #524

Inspirational Website of the Week: Kubikfoto³ A great design with wonderful details and smooth transitions. Our pick this week. Get inspired This content is sponsored via Syndicate Ads Website Builder Software With Your Branding This B2B platform fits designers, freelancers, agencies and anyone who Read more

Rundown Of Gen Z: What Does This Mean For Mobile Designers?

dreamt up by webguru in Uncategorized | Comments Off on Rundown Of Gen Z: What Does This Mean For Mobile Designers?

Rundown Of Gen Z: What Does This Mean For Mobile Designers?

Rundown Of Gen Z: What Does This Mean For Mobile Designers?

Suzanne Scacca



Just a few years ago, design blogs were all abuzz with tips on how to build websites for the millennial consumer. At this point, however, that’s old news. We know what millennials want from the online experience and have, for the most part, been able to meet their expectations.

But now we have a new generation of consumers to worry about: Generation Z.

Are they all that different from millennials?

As an old millennial myself, I can tell you that this next generation is one I don’t feel much of a connection to. I can remember a time when the most exciting part of my day was when my mom got off the phone and I was able to finally connect to the Internet with our dial-up AOL account.

This next generation doesn’t understand any of that. They weren’t caught between worlds the way many millennials were, which means they have a whole different set of expectations when it comes to the technology they interact with on a daily basis.

Consider this your quick and dirty guide to designing mobile websites for Gen Z.

What You Need to Know About Designing Mobile Web for Gen Z

According to Reality Bytes, a report published by WP Engine and The Center For Generational Kinetics, Gen Z cannot live without Internet access. More specifically:

  • 55% won’t go more than five hours without it.
  • 27% won’t go more than one.

Now, what’s really interesting about this particular generation is that, despite their attachment to the Internet, 61% of them prefer to shop on websites instead of mobile apps.

Considering how long they’ve had smart devices in their hands, it’s surprising that they’d gravitate towards browsers over native applications. That just goes to show you that native apps aren’t necessarily the best way to reach a mobile audience (especially if you want to make money from your app).

But all this is good news for you. Mobile apps are expensive to build and time-consuming to maintain. Plus, it’s quite difficult to convert users on mobile apps. If you were nervous about having to make the leap into mobile app development, or about losing money to those working in that field, don’t be. Generation Z wants your clients’ websites.

Now that you know which experience Gen Z is more likely to lean towards, it’s time to discuss designing for it.

Entertain Them

Before you do anything else, think about how to engage Gen Z visitors with entertainment.

According to Reality Bytes, entertainment is the primary reason why they visit the Internet:


Gen Z loves entertainment
Generation Z overwhelmingly prefers to be entertained online over anything else. (WP Engine) (Large preview)

Until now, the primary reason why consumers flocked to the Internet was for informational purposes. But that’s not all bad news for you as it means you have an excuse to reach out to older clients and say:

“Hey, times are changing! Here’s what’s on trend now and what your website needs in order to reach these new consumers.”

Just be careful: even for the most relaxed and youthful company, you don’t want to go over the top with adding entertaining elements into a mobile website. Just because 66% of Generation Z consumers name gaming as their #1 hobby doesn’t mean you need to build a mobile game and put it on your site. (Leave that to the gaming app developers.)

Instead, look at subtle ways to surprise Generation Z with something unexpected and engaging.

The first place to start? Video.

KFC has a great example of this right on its home page:


KFC funny video
KFC shares its well-known and hilarious video ads on its home page. (Source: KFC) (Large preview)

There’s a number of reasons why this video example is so awesome.

First, every visitor to the website — both on desktop and mobile — will see it right away. On mobile, visitors can’t even scroll down the home page. What you see above is what you get.

Next, this ensures your visitors get a taste of the brand’s humor and personality. Whereas content-driven sites or blogs can do that with GIFs and memes, Gen Z might not take the time to read content. Video makes sure they won’t miss out.

Also, this video is totally on brand, showcasing KFC’s humor in video form.

Even if you don’t have a humorous mascot to trot around, show off your client’s CEO or other engaging brand ambassador. Video is a great way to quickly suck Gen Z visitors in and let them know this is not going to be a dry or unmemorable experience.

If you don’t have video to play with, that’s okay. Think about adding humor as you educate visitors. Old Spice demonstrates:


Old Spice’s funny Ingredients pages
Old Spice’s informative, yet funny Ingredients pages. (Source: Old Spice) (Large preview)

Old Spice is another company that’s known for its entertaining video ads. However, their website also contains some humorous gems where you’d least expect them:

In the Ingredients section.

Visitors that are concerned about the ingredients within their deodorant can visit this page to learn more about what goes into each of the products. But read those descriptions closely. Not only are they transparent about what’s inside, but they also do it in a casual and playful way which says:

“Hey, we’re taking your concerns seriously. But don’t sweat it. We got you.”

Another way you can appeal to the entertainment-loving Generation Z is to add a social component to your site.

Don’t worry about building your own social network, community or forum either. Gen Z is going to flock to their favorite social platforms for that purpose: YouTube, Snapchat, Instagram, etc.

Instead, think about what it is that makes those environments so engage-worthy. Here’s an example from the Gymshark Central blog:


Gymshark blog post reactions
Gymshark uses social media-like reactions to improve blog engagement. (Source: Gymshark) (Large preview)

The blog post looks like any other. However, at the bottom of each post is this emoji reaction system. That’s pretty unique, right? Plus, this type of engagement doesn’t require much work from the reader. Simply click on the recognizable and well-labeled emoji and let the writer know what you thought of the content.

Another reason why this works so well is because Gymshark actively targets Gen Z customers. If you look on their website, you’ll see a section where they give discounts to college students.

So, if your client’s website needs to reach a younger audience, take a lesson from Gymshark. There’s no need to do a complete overhaul and convert an otherwise well-designed website into a superficial social network. Instead, add the most engaging elements from social networks and encourage your Gen Z visitors to interact more.

Personalize It

Think about what makes video games and social media so attractive to Gen Z. There’s obviously the fun aspect of it, but there’s another force at play that keeps users returning time and time again:

And that’s personalization.

This is something you should already be designing your mobile websites for as you target millennial consumers. However, if you haven’t bothered to add personalized touches to your sites, yet, you’re going to need to take this more seriously now.

Forbes Agency Council and YPulse worked together to find out what Generation Z really wants in terms of personalized experiences. Here’s a snippet from their findings:

“71% of the 3,000 respondents ages 14–29 said they prefer to receive offers that are customized to their location. In the same survey, 80% said they expect tailored onscreen experiences that not only target location but also recognize their interests and habits — who they are and how they self-identify.”

With 44% of Gen Z more likely to give up their personal data than other generations, you can’t afford to ignore this one.

One way to deliver a personalized experience based on what Gen Z visitors want is through push notifications. The only problem is, whether you have a mobile website or a progressive web app, push notifications are not supported on iOS devices.

While you can still use them to connect with Android users, consider other means for personalization that will help you connect with 100% of them.

Take, for instance, something as seemingly simple as what Amazon does with data:


Amazon home page personalization
About ¾ of the Amazon mobile home page contains personalized information. (Source: Amazon) (Large preview)

As you can see here, Amazon has saved a whole bunch of information about me:

  • My name and account information;
  • My cart items (which were not put there on this shopping trip);
  • My delivery location.

I also happen to own a Fire TV Stick, so this top ad is super relevant to me. Overall, I’m quite pleased with how tailor-made this first step inside the mobile website feels.

If you’re building a registration or membership system into your client’s website, make sure you’re leveraging user data to provide a personalized experience for them.

And if you don’t have a reason to ask mobile visitors to log in, that’s okay. You can still gather information by placing simply designed email subscription forms on the mobile website — preferably above the fold or at the very bottom. And most definitely on the home page.

Here’s a nice example from inkbox:


inkbox subscription form
inkbox uses a subscription form to start collecting information on visitors. (Source: inkbox) (Large preview)

inkbox has done a nice job with this subscription form. Even if mobile users scroll past the content on the page, they’re likely to see that white box screaming back at them, begging them to sign up.

Because there’s such a low commitment compared to the potential for such valuable rewards (like information on new products and access to exclusive discounts), Generation Z visitors will love this. Like I said earlier, they’re more than willing to give you their personal data if you can make it worth their while.

For mobile websites with a brick-and-mortar presence, geolocation features are non-negotiable. Generation Z shoppers often use their mobile devices while in-store, so this would definitely take their experience to the next level.

One of the easiest ways to implement this is with geolocation sensors like the ones used by The North Face:


The North Face geolocation features
The North Face auto-populates search parameters based on the user’s location. (Source: The North Face) (Large preview)

The above screenshot is what happens when I use the Find a Store feature on the mobile website. What’s great about this is it detected my location (which is currently Boston) and it auto-populated my search query with a nearby store.

Mobile users don’t have time to waste and Gen Z certainly won’t take kindly to a website that forces them to do all the work. Take what you know about your user — even if all you can detect is a location — and make their job of connecting with you easier.

Make Them Feel Special

Now, there’s a difference between adding personal touches to a website and taking it up a notch so that Generation Z visitors feel special. They’re going to need you to make them feel like their money and opinions and faces are worth more than anyone else’s.

You have to remember, this is the generation that grew up with reality TV and smartphone cameras pointed at their faces. That’s why it’s no surprise they’re known as the “influencer” generation.

On a related note, this makes Generation Z much more likely to build a relationship with brands as there’s the hope that, someday, they’ll reward their loyalty with a public-facing partnership.

Your clients will ultimately have to decide how far they want to take this. After all, affiliate partnerships and influencer marketing won’t make sense for everyone. That said, there are other ways to reap the advantages of having a generation of consumers willing to “help” a brand out.

According to data from the IBM Institute for Business Value and the National Retail Federation, 44% of Generation Z consumers said they’d be happy to share ideas for product design. That makes sense as that level of contribution will make them feel like they have more invested in the relationship with the company.

While your clients need to decide if consumers have any say in the shaping of their offering, you can do something about this from the web design side of things.

Check out what the Panera website is doing to solicit visitors for feedback:


Panera feedback request
Panera calls attention to its redesign and asks visitors for feedback. (Source: Panera) (Large preview)

These “Feedback” tabs are a somewhat new feature on the web, but more and more websites are certainly taking advantage of it. With this intro pop-up from Panera, it’s looking as though they want to make sure their visitors take notice, too.

This is a smart move in terms of trying to appeal to a younger audience. You let them know, straight out the gate, “We made some changes!” and then ask them to provide feedback. But that’s not all. Panera is saying: “Thanks for your help.” This implies that they want customers to take a greater part in shaping both the online experience and the brand itself.

There are other ways to make Generation Z feel like they’re more than just the average customer.

Sephora has created something called the “Rewards Bazaar” to layer on top of its more traditional BeautyInsider loyalty program:


Sephora Rewards Bazaar loyalty program
Rewards Bazaar is Sephora’s point-based rewards system that can be used to buy products and experiences. (Source: Sephora) (Large preview)

The Rewards Bazaar program works similarly to credit card points or airline miles programs. In other words, the more money customers spend with Sephora, the more points they accumulate.

And points don’t just buy them more Sephora goods. Points buy them experiences, like an Algenist R&R sleep retreat in Tucson, Arizona. They can also get “swag” bundles full of makeup and body products, clothes, artwork and more.

Sephora is rewarding their loyalty with celeb-like treats.

If you want to go crazy and actually encourage Gen Z customers to act like the influencers they so desire to be, take inspiration from this American Eagle example:


American Eagle’s unofficial influencer program
American Eagle asks customers to become unofficial influencers with AE x ME. (Source: American Eagle) (Large preview)

Generation Z customers already have their cameras ready to snap the next Insta-worthy photo, so why not give them a reason to put it to good use?

American Eagle has created an influencer-like program called AE x ME. Really, this is nothing more than a brilliant ploy to ask customers for user-generated content that American Eagle can then use on its website and beyond.

Whereas brands usually have to dig around for these types of photos or videos featuring their products in action, American Eagle is saying:

“You’re going to take pictures of your awesome new duds anyway. Why not let us show them off to a much larger audience for you?”

That’s not the only reward American Eagle dangles in front of influencers. While most of the people featured get a unique URL that features their Instagram handle and some information about them, there’s a profile at the bottom of this page that suggests there might be a greater opportunity here.


American Eagle influencer rewards
American Eagle turned a customer into a campaign model. (Source: American Eagle) (Large preview)

Daevin tagged American Eagle in one of his posts and then was flown out to become one of the faces in an upcoming campaign for the famous clothing brand. If that’s not living the dream for Gen Z influencer wannabes, I don’t know what is.

Keep It Real

One last thing to keep in the back of your minds as you design websites for Generation Z is the “keep it real” component. Anyone else remember this Dave Chappelle gem?

Chappelle Show keep it real GIF
Dave Chappelle struggles to keep it real, long before Gen Z had a say in it. (Source: Giphy)

Gen Z has very high standards when it comes to the authenticity of a company. And, not only that, they expect brands to be socially good, too.

Reality Bytes reports that:

  • 79% of Gen Z consumers respect brands more if they keep their images Photoshop-free.
  • 84% of them trust companies that put real customers in their ad campaigns (see American Eagle’s example above).
  • 69% of them will buy from companies that give money to and support good causes.

Really, what all of this data points to is this:

Generation Z wants to know that they’re engaging with genuinely good and honest people; not just a brand name.

That’s why, if your clients have a strong mission and values, their websites need to have one page specifically dedicated to them.

One way to do this is with a partnership highlight page like eos has:


eos recycling page
eos touts its recycling initiatives and partnership with TerraCycle. (Source: eos) (Large preview)

The eos website actually has two pages dedicated to this recycling cause. The first is called “Social Responsibility” where the company discusses its mission to build clean products. The second is called “TerraCycle” where it goes more into the recycling partnership.

For consumers looking to buy from companies that take causes like this seriously, these landing pages may be all they need to convince them to convert.

Samsung does something similar on its About Us page, except the key difference is that it calls out social responsibility and sustainability as part of its core company values:


Samsung social responsibility page
Samsung makes it its mission to make the environment a priority. (Source: Samsung) (Large preview)

This way, it’s not just Samsung telling customers where their products come from or where they end up. Instead, their entire identity becomes synonymous with this mission to create a better world.

When you compare them to companies that prioritize profit before anything else or who have been accused of being dishonest for the purposes of driving up their bottom line, Samsung stands out as a shining example of what to do.

Another way to stand out with this concept of social goodness is by showing customers how they’re contributing to a good cause by giving their business to them.

The Giving Keys is a brand that’s built its entire concept around this.


The Giving Keys social goodness
The Giving Keys highlights customer stories. (Source: The Giving Keys) (Large preview)

The Giving Keys isn’t just a website to buy jewelry from. This is a place where consumers can make a difference in more ways than one. It works like this:

  • Customers purchase a piece of jewelry and add a personalized inscription to the key charm.
  • They then give the inspirational message jewelry to someone who needs it. (The website features a section where customers can “Share Your Story” to talk about who they gave it to and why.)
  • In turn, the company uses a portion of the sale to help create jobs for the homeless.

This is the kind of thing that makes Generation Z stop and think twice about giving their money to a money-hungry mega brand instead of a smaller company that backs a good cause.

Wrapping Up

It’s an interesting thing, isn’t? Raise kids in a certain timeframe and environment and they’ll turn out to be completely different shoppers than the generations before them.

Thankfully, the same kinds of technologies that have powered Generation Z’s lives are the ones we can use to learn more about them. And we’ve certainly learned a lot. As you go about designing websites for clients in the future, don’t forget to bring up the Gen Z question to them. Even if they’re not selling to them now, someday in the not-so-distant future, they will.

Further Reading on SmashingMag:

Smashing Editorial
(ra, yk, il)

Source: Smashing Magazine, Rundown Of Gen Z: What Does This Mean For Mobile Designers?

Collective #503

dreamt up by webguru in Uncategorized | Comments Off on Collective #503



C503_mosaic

Mosaic

Mosaic is a declarative front-end JavaScript library for building user interfaces.

Check it out


C503_neort

NEORT (Beta)

NEORT is digital art platform for creators to share artworks and learn the skills beyond technology.

Check it out



C503_booleangame

The Boolean Game

A game for learning how to use boolean operations in Adobe Illustrator, Sketch, Figma, and other vector editors.

Play it














C503_indigo

indigo-player

A highly extensible, modern, TypeScript video player with support for the most popular stream formats, subtitles, out-of-the-box advertising, picture in picture, thumbnails and more.

Check it out






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


Source: Codrops, Collective #503

How To Align Things In CSS

dreamt up by webguru in Uncategorized | Comments Off on How To Align Things In CSS

How To Align Things In CSS

How To Align Things In CSS

Rachel Andrew



We have a whole selection of ways to align things in CSS today, and it isn’t always an obvious decision which to use. However, knowing what is available means that you can always try a few tactics if you come across a particular alignment problem.

In this article, I will take a look at the different alignment methods. Instead of providing a comprehensive guide to each, I’ll explain a few of the sticking points people have and point to more complete references for the properties and values. As with much of CSS, you can go a long way by understanding the fundamental things about how the methods behave, and then need a place to go look up the finer details in terms of how you achieve the precise layout that you want.

Aligning Text And Inline Elements

When we have some text and other inline elements on a page, each line of content is treated as a line box. The property text-align will align that content on the page, for example, if you want your text centered, or justified. Sometimes, however, you may want to align things inside that line box against other things, for example, if you have an icon displayed alongside text, or text of different sizes.

In the example below, I have some text with a larger inline image. I am using vertical-align: middle on the image to align the text to the middle of the image.

The line-height Property And Alignment

Remember that the line-height property will change the size of the line-box and therefore can change your alignment. The following example uses a large line-height value of 150px, and I have aligned the image to top. The image is aligned to the top of the line box and not the top of the text, remove that line-height or make it less than the size of the image and the image and text will line up at the top of the text.

It turns out that line-height and indeed the size of text is pretty complicated, and I’m not going to head down that rabbit hole in this article. If you are trying to precisely align inline elements and want to really understand what is going on, I recommend reading “Deep Dive CSS: Font Metrics, line-height And vertical-align.”

When Can I Use The vertical-align Property?

The vertical-align property is useful if you are aligning any inline element. This includes elements with display: inline-block. The content of table cells can also be aligned with the vertical-align property.

The vertical-align property has no effect on flex or grid items, and therefore if used as part of a fallback strategy, will cease to apply the minute the parent element is turned into a grid or flex Container. For example, in the next pen, I have a set of items laid out with display: inline-block and this means that I get the ability to align the items even if the browser does not have Flexbox:

See the Pen inline-block and vertical-align by Rachel Andrew.

In this next pen, I have treated the inline-block as a fallback for Flex layout. The alignment properties no longer apply, and I can add align-items to align the items in Flexbox. You can tell that the Flexbox method is in play because the gap between items that you will get when using display: inline-block is gone.

See the Pen inline-block flex fallback by Rachel Andrew.

The fact that vertical-align works on table cells is the reason that the trick to vertically center an item using display: table-cell works.

Now that we do have better ways to align boxes in CSS (as we will look at in the next section), we don’t need to employ the vertical-align and text-align properties in places other than the inline and text elements for which they were designed. However, they are still completely valid to use in those text and inline formats, and so remember if you are trying to align something inline, it is these properties and not the Box Alignment ones that you need to reach for.

Box Alignment

The Box Alignment Specification deals with how we align everything else. The specification details the following alignment properties:

  • justify-content
  • align-content
  • justify-self
  • align-self
  • justify-items
  • align-items

You might already think of these properties as being part of the Flexbox Specification, or perhaps Grid. The history of the properties is that they originated as part of Flexbox, and still exist in the Level 1 specification; however, they were moved into their own specification when it became apparent that they were more generally useful. We now also use them in Grid Layout, and they are specified for other layout methods too, although current browser support means that you won’t be able to use them just yet.

Therefore, next time someone on the Internet tells you that vertical alignment is the hardest part of CSS, you can tell them this (which even fits into a tweet):

.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

In the future, we may even be able to dispense with display: flex, once the Box Alignment properties are implemented for Block Layout. At the moment, however, making the parent of the thing you want centering a flex container is the way to get alignment horizontally and vertically.

The Two Types Of Alignment

When aligning flex and grid items, you have two possible things to align:

  1. You have the spare space in the grid or flex container (once the items or tracks have been laid out).
  2. You also have the item itself inside the grid area you placed it in, or on the cross axis inside the flex container.

I showed you a set of properties above, and the alignment properties can be thought of as two groups. Those which deal with distribution of spare space, and those which align the item itself.

Dealing With Spare Space: align-content And justify-content

The properties which end in -content are about space distribution, so when you choose to use align-content or justify-content you are distributing available space between grid tracks or flex items. They don’t change the size of the flex or grid items themselves; they move them around because they change where the spare space goes.

Below, I have a flex example and a grid example. Both have a container which is larger than required to display the flex items or grid tracks, so I can use align-content and justify-content to distribute that space.

See the Pen justify-content and align-content by Rachel Andrew.

Moving Items Around: justify-self, align-self, justify-items And align-items

We then have align-self and justify-self as applied to individual flex or grid items; you can also use align-items and justify-items on the container to set all the properties at once. These properties deal with the actual flex or grid item, i.e. moving the content around inside the Grid Area or flex line.

  • Grid Layout
    You get both properties as you can shift the item on the block and inline axis as we have a defined Grid Area in which it sits.
  • Flex Layout
    You can only align on the cross axis as the main axis is controlled by space distribution alone. So if your items are a row, you can use align-self to shift them up and down inside the flex line, aligning them against each other.

In my example below, I have a flex and a grid container, and am using align-items and align-self in Flexbox to move the items up and down against each other on the cross axis. If you use Firefox, and inspect the element using the Firefox Flexbox Inspector, you can see the size of the flex container and how the items are being moved vertically inside of that.


Flex items aligned in their container
Aligned flex items with the flex container highlighted in Firefox (Large preview)

In grid, I can use all four properties to move the items around inside their grid area. Once again, the Firefox DevTools Grid Inspector will be useful when playing with alignment. With the grid lines overlaid, you can see the area inside which the content is being moved:


Aligned grid items
Aligned grid items with the Grid highlighted in Firefox (Large preview)

Play around with the values in the CodePen demo to see how you can shift content around in each layout method:

See the Pen justify-self, align-self, justify-items, align-items by Rachel Andrew.

Confused By align And justify

One of the cited issues with people remembering the alignment properties in Grid and Flexbox, is that no one can remember whether to align or to justify. Which direction is which?

For Grid Layout, you need to know if you are aligning in the Block or Inline Direction. The Block direction is the direction blocks lay out on your page (in your writing mode), i.e. for English that is vertically. The Inline direction is the direction in which sentences run (so for English that is left to right horizontally).

To align things in the Block Direction, you will use the properties which start with align-. You use align-content to distribute space between grid tracks, if there is free space in the grid container, and align-items or align-self to move an item around inside the grid area it has been placed in.

The below example has two grid layouts. One has writing-mode: horizontal-tb (which is the default for English) and the other writing-mode: vertical-rl. This is the only difference between them. You can see that the alignment properties which I have applied work in exactly the same way on the block axis in both modes.

See the Pen Grid Block Axis Alignment by Rachel Andrew.

To align things in the inline direction, use the properties which begin with justify-. Use justify-content to distribute space between grid tracks, and justify-items or justify-self to align items inside their grid area in the inline direction.

Once again, I have two grid layout examples so that you can see that inline is always inline — no matter which writing mode you are using.

See the Pen Grid Inline Alignment by Rachel Andrew.

Flexbox is a little trickier due to the fact that we have a main axis which can be changed to row or column. So, let’s first think about that main axis. It is set with the flex-direction property. The initial (or default) value of this property is row which will lay the flex items out as a row in the writing mode currently in use — this is why when working in English, we end up with items laid out horizontally when we create a flex container. You can then change the main axis to flex-direction: column and the items will be laid out as a column which means they are laid out in the block direction for that writing mode.

As we can do this axis switching, the most important factor in Flexbox is asking, “Which axis is my main axis?” Once you know that, then for alignment (when on your main axis) you simply use justify-content. It doesn’t matter if your main axis is row or column. You control space between the flex items with justify-content.

See the Pen justfy-content in Flexbox by Rachel Andrew.

On the cross axis, you can use align-items which will align the items inside the flex container or flex line in a multi-line flex container. If you have a multi-line container using flex-wrap: wrap and have space in that container, you can use align-content to distribute the space on the cross axis.

In the example below, we are doing both with a flex container displayed as a row and a column:

See the Pen Cross axis alignment in Flexbox by Rachel Andrew.

When justify-content Or align-content Do Not Work

The justify-content and align-content properties in Grid and Flexbox are about distributing extra space. So the thing to check is that you have extra space.

Here is a Flex example: I have set flex-direction: row and I have three items. They don’t take up all of the space in the flex container, so I have spare space on the main axis, the initial value for justify-content is flex-start and so my items all line up at the start and the extra space is at the end. I am using the Firefox Flex Inspector to highlight the space.


Flex items aligned left, highlighted spare space on the right
The spare space at the end of the container (Large preview)

If I change flex-direction to space-between, that extra space is now distributed between the items:


Flex items aligned so space is distributed between the items
The spare space is now between the items (Large preview)

If I now add more content to my items so they become larger and there is no longer any additional space, then justify-content does nothing — simply because there is no space to distribute.


Flex items are filling the container with no spare space
There is now no space to distribute (Large preview)

A common question I’m asked is why justify-content isn’t working when flex-direction is column. This is generally because there is no space to distribute. If you take the above example and make it flex-direction: column, the items will display as a column, but there will be no additional space below the items as there is when you do flex-direction: row. This is because when you make a Flex Container with display: flex you have a block level flex container; this will take up all possible space in the inline direction. In CSS, things do not stretch in the block direction, so no extra space.


Flex items arranged as a column
The column is only as tall as needed to display the items (Large preview)

Add a height to the container and — as long as that is more than is required to display the items — you have extra space and therefore justify-content will work on your column.


A column of flex items with space between them.
Adding a height to the container means we have spare space (Large preview)

Why Is There No justify-self In Flexbox?

Grid Layout implements all of the properties for both axes because we always have two axes to deal with in Grid Layout. We create tracks (which may leave additional space in the grid container in either dimension,) and so we can distribute that space with align-content or justify-content. We also have Grid Areas, and the element in that area may not take up the full space of the area, so we can use align-self or justify-self to move the content around the area (or align-items, justify-items to change the alignment of all items).

Flexbox does not have tracks in the way that Grid layout does. On the main axis, all we have to play with is the distribution of space between the items. There is no concept of a track into which a flex item is placed. So there is no area created in which to move the item around in. This is why there is no justify-self property on the main axes in Flexbox.

Sometimes, however, you do want to be able to align one item or part of the group of items in a different way. A common pattern would be a split navigation bar with one item being separated out from the group. In that situation, the specification advises the use of auto margins.

An auto margin will take up all of the space in the direction it is applied, which is why we can center a block (such as our main page layout) using a left and right margin of auto. With an auto margin on both sides, each margin tries to take up all the space and so pushes the block into the middle. With our row of flex items, we can add margin-left: auto to the item we want the split to happen on, and as long as there is available space in the flex container, you get a split. This plays nicely with Flexbox because as soon as there is no available space, the items behave as regular flex items do.

Flexbox And Micro-Components

One of the things I think is often overlooked is how useful Flexbox is for doing tiny layout jobs, where you might think that using vertical-align is the way to go. I often use Flexbox to get neat alignment of small patterns; for example, aligning an icon next to text, baseline aligning two things with different font sizes, or making form fields and buttons line up properly. If you are struggling to get something to line up nicely with vertical-align, then perhaps try doing the job with Flexbox. Remember that you can also create an inline flex container if you want with display: inline-flex.

There is no reason not to use Flexbox, or even Grid for tiny layout jobs. They aren’t just for big chunks of layout. Try the different things available to you, and see what works best.

People are often very keen to know what the right or wrong way to do things is. In reality, there often is no right or wrong; a small difference in your pattern might mean the difference between Flexbox working best, where otherwise you would use vertical-align.

Wrapping Up

To wrap up, I have a quick summary of the basics of alignment. If you remember these few rules, you should be able to align most things with CSS:

  1. Are you aligning text or an inline element? If so, you need to use text-align, vertical-align, and line-height.
  2. Do you have an item or items you want to align in the center of the page or container? If so, make the container a flex container then set align-items: center and justify-content: center.
  3. For Grid Layouts, the properties that start with align- work in the Block direction; those which start with justify- work in the inline direction.
  4. For Flex Layouts, the properties that start with align- work on the Cross Axis; those which start with justify- work on the main axis.
  5. The justify-content and align-content properties distribute extra space. If you have no extra space in your flex or grid container, they will do nothing.
  6. If you think you need justify-self in Flexbox, then using an auto margin will probably give you the pattern you are after.
  7. You can use Grid and Flexbox along with the alignment properties for tiny layout jobs as well as main components — experiment!

For more information about alignment, see these resources:

Smashing Editorial
(il)

Source: Smashing Magazine, How To Align Things In CSS

Plesk VPS Hosting For Better Website Outcomes

dreamt up by webguru in Uncategorized | Comments Off on Plesk VPS Hosting For Better Website Outcomes

Plesk VPS Hosting For Better Website Outcomes

Plesk VPS Hosting For Better Website Outcomes

Suzanne Scacca



(This is a sponsored article.) In early discussions you have with clients about the website you’re tasked with designing, does the topic of web hosting ever come up? My guess is that it’s not something your clients give much thought to, waving it away with:

“Just give me the cheapest, no-frills hosting.”

I get why they’d think that way. For starters, they’re paying you a large sum of money to design the site. Of course, they’re going to look to other areas to offset those costs. Because server technology is rarely understood by the people who own websites, it’s easy for them to mistakenly think they can save money there.

Here’s the problem though:

As a website grows in authority and expands its reach, security and performance problems will arise if the hosting configuration isn’t prepared to handle it.

In the following article, I’m going to show you why clients need the power of VPS hosting behind the websites you design for them. And why you — the administrator — need a tool like the Plesk control panel to manage it.

The Web Designer’s Connection to Web Hosting

In many cases, people get stuck being the go-to person for one highly specialized task at the companies they work for. This person handles the marketing. This person manages inventory. This person coordinates client meetings.

One of the things I love about working with websites is that there are so many new things to learn about and other areas to branch out to. If you don’t want to be relegated to building website after website, day in and day out, there are ways to expand your offering and become the total end-to-end solution provider for clients.

In my opinion, web hosting is one of the areas you should look to for expansion. Now, I’m not saying you should become a reseller of hosting or anything like that. All I mean is that it would be beneficial to understand how the technology behind a website affects the outcomes of what you’ve built.

For example:

  • An underpowered hosting plan fails to handle influxes of traffic, which leads to slower page speeds and a drop in conversion rates.
  • Occasional downtime on the website leaves visitors wondering if it’s even worth going to the site if its availability is unreliable.
  • There’s a high demand for the inventory sold on the site, but potential customers are too nervous to pull the trigger since security seems to be non-existent.

Even if you’re not the one responsible for the server technology your client’s site sits on, you can see how this sort of thing could have a negative effect on your business. You build an amazing website that aims to do exactly what your client wanted, but the server technology is holding it back.

Who do you think the client is going to blame in that case?

Rather than let it get that far, I’d suggest you engage your clients early on in conversations about their web hosting. As we move on, I’m going to present you with specific arguments you should be prepared to make regarding the hosting as well as how it’s managed.

An Introduction To VPS Hosting

When it comes to choosing the right hosting for your clients’ websites, there’s a lot to think about:

Who Should You Entrust Your Website To?

There are thousands of web hosting companies to choose from. But in terms of reliability? That list could easily be narrowed down to less than a hundred.

HostGator has been a web hosting company I’ve recommended to clients for years, especially ones who need a powerful hosting solution like Plesk VPS hosting. If your client doesn’t have a strong preference of provider, start here.

What Type Of Web Hosting Will Serve Your Website And Audience Best?

This is what you need to know about the different kinds of web hosting:

Shared Hosting

This is the cheapest form of hosting available and probably the one your clients will be most inclined to purchase.

The hosting provider designates a section of a web server to a number of clients who will then share the resources. This means there are strict limitations set on how much bandwidth and storage a website can use, but very little you can do to control any of it — especially if another website in the shared server space hogs the resources.

It’s this last point that’s especially problematic on shared hosting. Although your hosting plan might indicate you get X amount of memory, it’s actually a cap on how much you might have access to if no one else is using resources from the server at the same time. In reality, it’s very likely you’ll run into lack of memory errors due to this limitation quite frequently.

Shared hosting is fine for small, personal blogs or private websites. Not for serious businesses.

Cloud Hosting

This is similar to shared hosting except that it’s more secure and stable.

Rather than relegate a website to one specific segment of a web server, the site is hosted across a number of servers. That way, if one server experiences an outage or another website compromises the performance of others around it, your website can safely be hosted elsewhere.

That said, there are still a number of limitations that come from cloud hosting. If your website is for a growing business, but you don’t expect a lot of traffic to it (say, if it were a simple portfolio), cloud hosting would be a good choice.

Dedicated Hosting

This is the most robust form of web hosting, which also makes it the most expensive.

As the name indicates, your hosting company will lease you an entire server to host your website. So, think of this like shared hosting, but on steroids. As you can imagine, when you have your own server environment, it greatly reduces the risk of anyone else compromising the performance of your website.

That said, there is a lot more work involved in managed a dedicated hosting account and the website on it. This is really only best for large enterprises, social networks, e-commerce sites and others that require this type of extreme web hosting.

VPS Hosting

This stands for “virtual private server”. The name alone should give you a good idea of how this differs from the other kinds of hosting already mentioned.

In sum, a virtual private server is like a scaled-back version of dedicated hosting. Instead of having an entire server to yourself, the web hosting company carves out a dedicated portion of the server and personalizes its settings for you. Although you share the server with other VPS clients, you don’t share the resources with anyone else. You get exactly what you pay for.

Here are some other highlights of VPS hosting:

  • It’s faster and more secure than shared or cloud hosting.
  • It’s cheaper than dedicated hosting.
  • It’s custom-tailored to your needs, but still allows you to take more control over your server configuration.

Bottom line: VPS is an overall better hosting solution for growing businesses.

How Will You Manage Your Web Hosting Account?

There’s one more question you have to ask yourself before you commit to a new hosting provider and plan.

Because VPS hosting is more complex and requires a greater degree of management than a set-it-and-forget-it type of hosting like shared or cloud, you need a control panel you can rely on.

So, let’s explore the Plesk panel and take a look at what you can do to maximize the management of your new website with it.

An Exploration Of Plesk VPS Hosting

This is the Plesk website:


A screenshot of the Plesk website frontpage
Large preview

It won’t take long to realize that Plesk is not like other control panel solutions. The website will help clarify some of this for you, but I’d like to give you an inside look at the control panel so you can see for yourself.

A Universally Friendly Control Panel

Plesk is one of those tools you step inside of and immediately realize you made the right choice. With a very short learning curve, Plesk is a highly intuitive control panel solution that’s great for anyone:


A screenshot of the Plesk website showing different options and that it is a universally user-friendly platform
Plesk is a universally user-friendly platform. (Image source: Large preview)

In all honesty, I don’t know how much time your clients will spend inside the control panel. When I’ve managed and built websites for clients in the past, just asking for login credentials to their hosting account tended to be a real chore.

“What’s hosting? Is that WordPress? I don’t think I need that.”

Regardless of whether they want or know what to do with a control panel, Plesk provides a user-friendly experience regardless of who the user is as well as their level of comfort with website management. I’ll show you why in this next example.

Words cannot describe how frustrating it is to ask clients to complete simple tasks. (Source)

Great Interface For Clients And Other End Users

If you’ve ever tried to use cPanel to manage hosting and domain services, you know how overwhelming it can be to use.

If you’re not familiar with it, this is typically what cPanel looks like upon first logging in:


Screenshot of the cPanel dashboard
This is how the cPanel interface (meant for end users) looks like. (Image source: cPanel) (Large preview)

If you plan on reselling or managing hosting for your cPanel clients, then you’ll need to use a separate dashboard called WHM:


cPanel’s user interface
cPanel’s user interface (Image source: cPanel) (Large preview)

There’s a navigation and sub-navigation bar at the top, which makes management options seem simple enough.

Then, you’re presented with individual actions you can take to manage hosting, your website or email accounts within the control panel itself. This is just too much — even for technically-minded clients who know what the heck they’re looking for.

Now, check out the Plesk interface for power users:


Plesk power user UI
The Plesk power user home page. (Source: Plesk) (Large preview)

This is insanely well-organized and clearly labeled. If your clients or other novice users were to step inside of Plesk, they’d instantly know where to go as well as which actions they could possibly take from the sidebar alone.

It gets better within each of the individual modules. For instance:


Clean UI inside Plesk
An example of the clean layout of the Plesk UI. (Source: Plesk) (Large preview)

This is what the Tools & Settings page looks like. As you can see, it’s not bogged down by a barrage of icons for each setting. Instead, it presents options in a succinct and well-organized manner, which will greatly reduce friction that might otherwise exist in a tool of this nature.

Great Interface For Designers And Developers

Plesk offers an alternative “service provider” view for web developers and designers:


Plesk UI for developers
A look at the Plesk service provider interface for developers. (Source: Plesk) (Large preview)

It looks a lot like the power user view, except you can see that the sidebar is broken up into different modules. What I like about this is that it encourages developers to manage more of their business from one tool instead of a variety of business management tools.

From within Plesk, you can:

  • Add new customer accounts and manage them from one dashboard.

Plesk add users
Adding and managing customers in Plesk is easy. (Source: Plesk) (Large preview)
  • Customize what they do and see in their “power user” view of Plesk. This helps keep server and website management under control.

Plesk user management
Managing customers in Plesk is easy. (Source: Plesk) (Large preview)
  • Create hosting plans that you can, in turn, sell to customers as subscriptions.

Plesk hosting plan management
Managing hosting plans in Plesk is easy. (Source: Plesk) (Large preview)
  • Move non-Plesk customers over to Plesk with a simple-to-use migration tool.

Plesk migration
The Plesk Migrator extension. (Source: Plesk) (Large preview)
  • Customize nearly every aspect of your clients’ server configurations. Like disk space:

Plesk server configuration
It’s easy to configure your server with Plesk. (Source: Plesk) (Large preview)
  • Manage the essentials to ensure the server runs in tip-top shape. For instance, here are some of the PHP settings for security and performance:

Plesk security and performance
Security and performance controls are a priority in Plesk. (Source: Plesk) (Large preview)
  • Manage things like plugins, themes and more if you build websites with WordPress.

Plesk WordPress controls
Plesk users can control various WordPress settings and tools inside of the control panel. (Source: Plesk) (Large preview)

If you’ve ever been frustrated with the management piece of your business or felt that your ability to control things was lacking, Plesk is the solution. Plus, you can use Plesk extensions to really open this tool up. Add business management features like invoicing and site-builder tools to improve your offering, streamline your workflow and make more money.

Last but not least, you can white label it with your branding. That way, when clients step inside, they’re reminded that they have a trusted website pro like you to properly manage their server and website.

Flexible Workflows

Another developer-friendly feature of Plesk is its flexibility.

One of the issues with letting clients make decisions about their web hosting and server management is that they don’t understand the amount of work that goes into it behind the scenes. They might think:

“You’re the developer. Why can’t you work with whatever I give you?”

But you and I know it’s not that simple.

For starters, there’s your level of comfort in using the command line. For some developers, that level of comfort is low, so having a flexible solution like Plesk that removes the need for programming is great.

That said, you can still use the CLI if you prefer. Plesk provides you with full root access, so you won’t have to worry about being restricted to the control panel’s settings to manage your VPS server either. Like I said, it’s flexible. It allows you to work as you want to work.

Plus, it works on a number of configurations:

  • Linux vs. Windows
  • Apache vs. nginx
  • Ruby on Rails vs. Node.js.

Whatever you choose, settings are available to deeply customize and configure each so that the VPS hosting plan works exactly as you need it to.

Wrapping Up

It’s your hope that when you build a website for a client, it doesn’t go to waste. You design powerful website experiences so that clients can effectively leverage their web presences to drum up new business and increase conversions.

Sadly, something like a poor choice of web hosting can compromise all of that planning and hard work on your part. Unless you’re in the habit of designing websites for very small businesses or nonprofits, Plesk VPS Hosting is the logical choice. Not only is it a great solution in terms of easing your administration and management responsibilities, but it’s also an amazing tool for building your design business.

If you’re interested in using Plesk VPS hosting, I’d suggest you start by looking at HostGator. In addition to being one of the leading hosting companies around the world, there is a 45-day Money-Back Guarantee available which may help you encourage your clients to give it a try.

Smashing Editorial
(ms, ra, yk, il)

Source: Smashing Magazine, Plesk VPS Hosting For Better Website Outcomes

Building Real-Time Charts With GraphQL And Postgres

dreamt up by webguru in Uncategorized | Comments Off on Building Real-Time Charts With GraphQL And Postgres

Building Real-Time Charts With GraphQL And Postgres

Building Real-Time Charts With GraphQL And Postgres

Rishichandra Wawhal



Charts form an integral part of any industry that deals with data. Charts are useful in the voting and polling industry, and they’re also great at helping us better understand the different behaviors and characteristics of the users and clients we work with.

Why are real-time charts so important? Well, they’re useful in cases when new data is produced continuously; for example, when using live-time series for visualizing stock prices is a great use for real-time charts. In this tutorial, I’ll explain how to build real-time charts with open-source technologies apt for exactly this particular task.

Note: This tutorial requires basic knowledge of React and GraphQL.

Stack

  1. PostgreSQL
    The very point behind using Charts is to visualize “huge” volumes data. We, therefore, need a database that efficiently handles large data and provides an intuitive API to restructure it. SQL databases allow us to make views that abstract and aggregate data for us. We will be using Postgres which is a time-tested and highly efficient database. It also has fancy open-source extensions like Timescale and PostGIS which allow us to build geolocation-based and time-series-based charts respectively. We will be using Timescale for building our time series chart.
  2. GraphQL Engine
    This post is about building real-time charts, and GraphQL comes with a well-defined spec for real-time subscriptions. Hasura GraphQL Engine is an open-source GraphQL server that takes a Postgres connection and allows you to query the Postgres data over realtime GraphQL. It also comes with an access control layer that helps you restrict your data based on custom access control rules.
  3. ChartJS
    ChartJS is a popular and well maintained open source library for building charts with JavaScript. We will use chart.js along with its ReactJS abstraction react-chartjs-2. About why React, it is because React empowers developers with an intuitive event-driven API. Also, React’s unidirectional data flow is ideal for building charts that are data-driven.

Requirements

For this tutorial, you will need the following on your system:

  1. Docker CE
    Docker is a software that lets you containerize your applications. A docker image is an independent packet that contains software along with its dependencies and a minimalistic operating system. Such docker images can be technically run in any machine that has docker installed. You will need docker for this tutorial.

  2. npm: npm is the package manage for JavaScript.

Demo

We will build the following live time series chart that shows the maximum temperature of a location in intervals of 5 seconds over the past 20 minutes from the present moment.

GIF Demo of the realtime chart
GIF Demo of the realtime chart

Setting Up The Backend

Running The Services

The backend comprises of a Postgres database, its timescale extension, and Hasura GraphQL Engine. Let us get the database and our GraphQL server running by running the respective docker images. Create a file called docker-compose.yaml and paste this content into it.

Note: docker-compose is a utility to run multiple docker images declaratively.

version: '2'
services:
  timescale:
    image: timescale/timescaledb:latest-pg10
    restart: always
    environment:
      POSTGRES_PASSWORD: postgrespassword
    volumes:
    - db_data:/var/lib/postgresql/data
  graphql-engine:
    image: hasura/graphql-engine:v1.0.0-alpha38
    ports:
    - "8080:8080"
    depends_on:
    - "timescale"
    restart: always
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@timescale:5432/postgres
      HASURA_GRAPHQL_ACCESS_KEY: mylongsecretkey
    command:
      - graphql-engine
      - serve
      - --enable-console
volumes:
  db_data:

This docker-compose.yaml contains the spec for two services:

  1. timescale
    This is our Postgres database with Timescale extension installed. It is configured to run at port 5432.
  2. graphql-engine
    This is our Hasura GraphQL Engine instance, i.e. the GraphQL server that points to the database and gives GraphQL APIs over it. It is configured to run at the port 8080, and the port 8080 is mapped to the port 8080 of the machine that this docker container runs on. This means that you can access this GraphQL server through at localhost:8080 of the machine.

Let’s run these docker containers by running the following command wherever you have placed your docker-compose.yaml.

docker-compose up -d

This command pulls the docker images from the cloud and runs them in the given order. It might take a few seconds based on your internet speed. Once it is complete, you can access your GraphQL Engine console at http://localhost:8080/console.


Hasura GraphQL Engine Console
Hasura GraphQL Engine console (Large preview)

Setting Up The Database

Next, let us create a table called temperature that stores the values of temperatures at different times. Go to the Data tab in the console and go to the SQL section. Create our temperature table by running this SQL block:

CREATE TABLE temperature (
  temperature numeric not null,
  location text not null,
  recorded_at timestamptz not null default now()
);

This creates a simple Postgres table in the database. But we wish to leverage the time interval partitioning of the Timescale extension. To do this, we must convert this table into timescale’s hypertable by running the SQL command:

SELECT create_hypertable('temperature', 'recorded_at');

This command creates a hypertable that is partitioned by time in the field recorded_at.

Now, since this table is created, we can directly start making GraphQL queries over it. You can try them out by clicking on the GraphiQL tab on top. Try making a mutation first:

mutation {
  insert_temperature (
    objects: [{
      temperature: 13.4
      location: "London"
    }]
  ) {
    returning {
      recorded_at
      temperature
    }
  }
}

The GraphQL mutation above inserts a row in the temperature table. Now try to make a GraphQL query to check if the data was inserted.

Then try making a query:

query {
  temperature {
    recorded_at
    temperature
    location
  }
}

Hope it worked 🙂

Now, the task at our hand is to create a live time-series chart that shows the maximum temperature of a location in intervals of 5 seconds over the past 20 minutes from the present moment. Let’s create a view that gives us exactly this data.

CREATE VIEW last_20_min_temp AS (
  SELECT time_bucket('5 seconds', recorded_at) AS five_sec_interval,
  location,     
    MAX(temperature) AS max_temp
  FROM temperature
  WHERE recorded_at > NOW() - interval '20 minutes'    
  GROUP BY five_sec_interval, location    
  ORDER BY five_sec_interval ASC
);

This view groups the data from the temperature table in 5-second windows with their max temperature (max_temp). The secondary grouping is done using the location field. All this data is only from the past twenty minutes from the present moment.

That’s it. Our backend is set up. Let us now build a nice real-time chart.

Frontend

Hello GraphQL Subscriptions

GraphQL subscriptions are essentially “live” GraphQL queries. They operate over WebSockets and have exactly the same response structure like GraphQL queries. Go back to http://localhost:8080/console and try to make a GraphQL subscription to the view we created.

subscription {
  last_20_min_temp(
    order_by: {
      five_sec_interval: asc
    }
    where: {
      location: {
        _eq: "London"
      }
    }
  ) {
    five_sec_interval
    location
    max_temp
  }
}

This subscription subscribes to the data in the view where the location is London and it is ordered in ascending order of the five_second_intervals.

Naturally, the response from the view would be an empty array because we have not inserted anything in the database in the past twenty minutes. (You might see the entry that we inserted sometime back if you reached this section within twenty minutes.)

{
  "data": {
    "last_20_min_temp": []
  }
}

Keeping this subscription on, open another tab and try inserting another value in the temperatures table using the same mutation that we performed earlier. After inserting, if you go back to the tab where the subscription was on, you would see the response having updated automatically. That’s the realtime magic that GraphQL Engine provides. Let’s use this subscription to power our real-time chart.

Getting Started With Create-React-App

Let us quickly get started with a React app starter using create react app. Run the command:

npx create-react-app time-series-chart

This will create an empty starter project. cd into it and install the GraphQL and chart libraries. Also, install moment for converting timestamps to a human-readable format.

cd time-series-chart
npm install --save apollo-boost apollo-link-ws subscriptions-transport-ws graphql react-apollo chart.js react-chartjs-2 moment

Finally, run the app with npm start and a basic React app would open up at http://localhost:3000.


Raw create-react-app
Raw creat-react-app (Large preview)

Setting Up Apollo Client For Client-Side GraphQL

Apollo client is currently the best GraphQL client that works with any GraphQL compliant server. Relay modern is good too but the server must support the relay spec to leverage all the benefits of Relay modern. We’ll use Apollo client for client-side GraphQL for this tutorial. Let us perform the setup to provide Apollo client to the app.

I am not getting into the subtleties of this setup because the following code snippets are taken directly from the docs. Head to src/index.js in the React app directory and instantiate Apollo client and add this code snippet above ReactDOM.render.

import { WebSocketLink } from 'apollo-link-ws';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';

// Create a WebSocket link:
const link = new WebSocketLink({
  uri: 'ws://localhost:8080/v1alpha1/graphql',
  options: {
    reconnect: true
  }
});
const cache = new InMemoryCache();
const client = new ApolloClient({
  link,
  cache
});

Finally, wrap the App inside ApolloProvider so that we can use Apollo client in the children components. Your App.js should finally look like:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { WebSocketLink } from 'apollo-link-ws';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';

// Create a WebSocket link:
const link = new WebSocketLink({
  uri: `ws://localhost:8080/v1alpha1/graphql`,
  options: {
    reconnect: true
  }
});
const cache = new InMemoryCache();
const client = new ApolloClient({
  link,
  cache
});

ReactDOM.render(
  (
    <ApolloProvider client={client}> 
      <App />
    </ApolloProvider>
  ),
  document.getElementById('root')
);

Apollo client has been set up. We can now easily use real-time GraphQL from our App. Head to src/App.js.

Building The Chart

ChartJS provides a pretty neat API for building charts. We will be building a line chart; so a line chart expects data of the form:

{
  "labels": ["label1", "label2", "label3", "label4"],
  "datasets": [{
    "label": "Sample dataset",
    "data": [45, 23, 56, 55],
    "pointBackgroundColor": ["red", "brown", "green", "yellow"],
    "borderColor": "brown",
    "fill": false
  }],
}

If the above dataset is used for rendering a line chart, it would look something like this:


Sample line chart
Sample line chart (Large preview)

Let us try to build this sample chart first. Import Line from react-chartjs-2 and render it passing the above object as a data prop. The render method would look something like:

render() {
  const data = {
    "labels": ["label1", "label2", "label3", "label4"],
    "datasets": [{
      "label": "Sample dataset",
      "data": [45, 23, 56, 55],
      "pointBackgroundColor": ["red", "brown", "green", "yellow"],
      "borderColor": "brown",
      "fill": false
    }],
  }
  return (
    
); }

Next, we will subscribe to the data in our view and feed it to the Line chart. But how do we perform subscriptions on the client?

Apollo’s <Subscription> components work using the render prop pattern where the children of a component are rendered with the context of the subscription data.

<Subscription
  subscription={gql`subscription { parent { child } }`}
/>
  {
    ({data, error, loading}) => {
      if (error) return <Error error={error} />;
      if (loading) return <Loading />;
      return <RenderData data={data} />;
    }
  }
</Subscription>

Let us use one such Subscription component to subscribe to our view and then transform the subscription data to the structure that ChartJS expects. The transforming logic looks like this:

let chartJSData = {
  labels: [],
  datasets: [{
    label: "Max temperature every five seconds",
    data: [],
    pointBackgroundColor: [],
    borderColor: 'brown',
    fill: false
  }]
};
data.last_20_min_temp.forEach((item) => {
  const humanReadableTime = moment(item.five_sec_interval).format('LTS');
  chartJSData.labels.push(humanReadableTime);
  chartJSData.datasets[0].data.push(item.max_temp);
  chartJSData.datasets[0].pointBackgroundColor.push('brown');
})

Note: You can also use the open-source library graphq2chartjs for transforming the data from GraphQL response to a form that ChartJS expects.

After using this inside the Subscription component, our App.js looks like:

import React, { Component } from 'react';
import { Line } from 'react-chartjs-2';
import { Subscription } from 'react-apollo';
import gql from 'graphql-tag';
import moment from 'moment';

const TWENTY_MIN_TEMP_SUBSCRIPTION= gql'
  subscription {
    last_20_min_temp(
      order_by: {
        five_sec_interval: asc
      }
      where: {
        location: {
          _eq: "London"
        }
      }
    ) {
      five_sec_interval
      location
      max_temp
    }
  }
'

class App extends Component {
  render() {
    return (
      
{ ({data, error, loading}) => { if (error) { console.error(error); return "Error"; } if (loading) { return "Loading"; } let chartJSData = { labels: [], datasets: [{ label: "Max temperature every five seconds", data: [], pointBackgroundColor: [], borderColor: 'brown', fill: false }] }; data.last_20_min_temp.forEach((item) => { const humanReadableTime = moment(item.five_sec_interval).format('LTS'); chartJSData.labels.push(humanReadableTime); chartJSData.datasets[0].data.push(item.max_temp); chartJSData.datasets[0].pointBackgroundColor.push('brown'); }) return ( ); } }
); } } export default App;

You will have a fully working real-time chart ready at http://localhost:3000 . However, it would be empty, so let’s populate some sample data so we can actually see some magic happen.

Note: I have added some more options to the Line chart because I don’t like those fancy animations in ChartJS. A time series looks sweet when it’s simple, however, you can remove the options prop if you like.

Inserting Sample Data

Lets write a script that populates our database with dummy data. Create a separate directory (outside this app) and create a file called script.js with the following content,

const fetch = require('node-fetch');
setInterval(
  () => {
    const randomTemp = (Math.random() * 5) + 10;
    fetch(
      `http://localhost:8080/v1alpha1/graphql`,
      {
        method: 'POST',
        body: JSON.stringify({
          query: `
            mutation ($temp: numeric) {
              insert_temperature (
                objects: [{
                  temperature: $temp
                  location: "London"
                }]
              ) {
                returning {
                  recorded_at
                  temperature
                }
              }
            }
          `,
          variables: {
            temp: randomTemp
          }
        })
      }
    ).then((resp) => resp.json().then((respObj) => console.log(JSON.stringify(respObj, null, 2))));
  },
  2000
);

Now run these two commands:

npm install --save node-fetch
node script.js

You can go back to http://localhost:3000 and see the chart updating.

Finishing Up

You can build most of the real-time charts using the ideas that we discussed above. The algorithm is:

  1. Deploy GraphQL Engine with Postgres;
  2. Create tables where you wish to store data;
  3. Subscribe to those tables from your React app;
  4. Render the chart.

You can find the source code here.

Smashing Editorial
(dm, ra, il)

Source: Smashing Magazine, Building Real-Time Charts With GraphQL And Postgres

Meet “Art Direction For The Web,” A New Smashing Book By Andy Clarke

dreamt up by webguru in Uncategorized | Comments Off on Meet “Art Direction For The Web,” A New Smashing Book By Andy Clarke

Meet “Art Direction For The Web,” A New Smashing Book By Andy Clarke

Meet “Art Direction For The Web,” A New Smashing Book By Andy Clarke

Bruce Lawson



A page on the Web isn’t like a printed page. Many of us learned that the hard way when we abandoned fixed-width layouts and embraced the web’s inherent flexibility and responsiveness. Read the excerpt chapter.

Modern web technologies like CSS Grid, Flexbox and Shapes have made it possible for us to implement print’s often distinctive designs, and the web’s now full of tutorials on how to use them. But the most important question is not “how” we can use art direction techniques to improve our designs for the web, but instead “when” and “why”.


A hardcover book laying on a wooden floor, called “Art Direction for the Web”

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle.
Free for Smashing Members.

Hardcover

$29 $39Get the Print (incl. eBook)

Printed, quality hardcover.
Shipping starts mid-April.

This is the reason why Andy Clarke wrote his new book Art Direction for the Web. This is a book about why art direction matters and how you can art-direct compelling and effective experiences across devices and platforms.

Andy explores the work of some of the most influential art directors, luminaries like Alexey Brodovitch, Bea Feitler, and Neville Brody. He doesn’t encourage us to merely mimic work from a previous era and medium, but to understand their thinking and learn how to apply that knowledge to art direction for the web.

Andy writes,

“You needn’t have been to art school to learn and apply the principles I teach you. Just like art direction itself, they’re something which everyone — no matter what your background and current area of expertise — can use every day to improve the effectiveness of a product or website’s design.”

Andy’s goal is to teach people about the importance of art direction for the web and explain how art direction can help people tell stories by using design. That way, products and websites will connect with audiences and also manage to keep them engaged. After a thorough investigation of the methodology of art direction, Andy teaches how to accomplish it by embracing the web using modern CSS.

Art Direction for the Web will help you make your sites more effective at communicating, persuading, and selling. If you develop products, this book will make them more compelling and more enjoyable to use. Read the excerpt chapter →


A hardcover book laying open on a light blue background, with two pages open that are showing the table of contents

Table Of Contents

Part 1: Explaining Art Direction

  • What Art Direction Means
    Ask what art direction means to developers, and they might answer: using the <picture> element or sizes attribute in HTML for responsive images; presenting alternative crops, orientations, or sizes at various screen sizes. But there’s more to it.
  • One Hundred Years Of Art Direction
    Bradley, Brodovitch, Brody, and Feitler — together, their names sound like a Mad Men-era advertising agency. In this chapter, we’ll take a look at their iconic works, from the 1930’s to the 1980’s.
  • Art-Directing Experiences
    Whether we write fact or fiction, sell or make products, the way to engage people, create desire, make them happy, and encourage them to stay that way, is by creating narratives. So what do we need to consider when doing so?
  • Art Direction And Creative Teams
    Let’s take a look at how we can embrace collaboration and form teams who follow strategies built around common goals.

A hardcover book laying open on a light blue background, with two pages open that are showing some artwork examples from the 1930s, by Alexey Brodovitch

Part 2: Designing For Art Direction

  • Principles Of Design
    Are the principles which have guided design in other media for generations relevant to the world of digital products and websites? Of course! In this chapter, we’ll explore the principles of symmetry, asymmetry, ratios, and scale.
  • Directing Grids
    Grids have a long and varied history in design, from the earliest books, through movements like constructivism right up to the present-day popularity of grids in frameworks like Bootstrap and material design. This chapter explains grid anatomy and terminology and how to use modular and compound grids.
  • Directing Type
    White space, typographic scale, and creative uses of type are the focus in this chapter.
  • Directing Pictures
    Images and how we display them have an enormous impact on how people perceive our designs, whether that be on a commercial or editorial website, or inside a product. In this chapter, you’ll learn how to position and arrange images to direct the eye.

A hardcover book laying open on a light blue background, with two pages open that are showing some artwork examples of Bond conference and Medium Memberships

Part 3: Developing For Art Direction

  • Developing Layouts With CSS Grids
    CSS Grid plus thoughtful, art-directed content offers us the best chance yet of making websites which are better at communicating with our audiences. In this chapter, Andy explains properties and techniques which are most appropriate for art direction.

  • Developing Components With Flexbox
    While Grid is ideal for implementing art-directed layouts, Flexbox is often better suited to developing molecules and organisms such as navigation links, images, captions, search inputs, and buttons. This chapter explores how to make use of it.

  • Developing Typography
    From multi-column layout and arranging type with writing modes to text orientation and decorative typography, this chapter dives deep into the code side of type.

  • Developing With Images
    How do you fit your art-directed images to a user’s viewport? And what do CSS shapes and paths have in store for your design? Let’s find out in this final chapter.

A hardcover book laying open on a light blue background, with two pages open that are showing some artwork examples of Bond conference and Medium Memberships

Smashing TV Webinars

To accompany this book, Andy is also giving a series of webinars on Smashing TV. Webinars are free with Smashing Membership, which costs a couple of cups of coffee a month (cancel anytime).

Andy Clarke is running a webinar on Art Direction.

About The Author

Andy Clarke is a well-known designer, design consultant, and mentor. He has been called plenty of things since he started working on the web. His ego likes terms such as “Ambassador for CSS,” “industry prophet,” and “inspiring,” but he’s most proud that Jeffrey Zeldman once called him a “triple-talented bastard.”

With his wife, Sue, Andy founded Stuff & Nonsense in 1998. They’ve helped companies around the world to improve their designs by providing consulting and design expertise.

Andy’s written several popular books on website design and development, including Hardboiled Web Design: Fifth Anniversary Edition, Hardboiled Web Design, and Transcending CSS: The Fine Art Of Web Design. He’s a popular speaker and gives talks about art direction and design-related topics all over the world.

Testimonials

It has been our goal to make the book as inspiring, practical, and useful as possible, and we feel honored to have already received such positive reviews.

“With ‘Art Direction for the Web,’ Andy provides a framework for harnessing the web’s potential. With historical context and real-life examples, Andy inspires each of us to be more purposeful about the choices we make. And true to form, he follows up all that inspiration with demos and the practical knowledge needed to see our ideas manifest online.”

— Trent Walton, co-founder of Paravel Inc.

Why This Book Is For You

The content of this book is based on Andy’s twenty years’ experience of working with clients, plus the expertise of the art directors and designers he interviewed. You’ll learn:

  1. What art direction means, why it matters, and who can do it.
  2. How to make art direction work for digital products and websites.
  3. How to improve conversions and bring your customers’ journeys to life.
  4. How to maintain brand values and design principles by connecting touch points across marketing, product design, and websites.
  5. How to use art direction priciples such as layout, typography, proportions, ratio, and grids in a more imaginative way to communicate what you’re trying to do much better.
  6. How to implement your designs on any platform with the latest HTML and CSS.
  7. …Plus, we’ll explore outstanding designs from 100 years of media and print publishing for some extra art direction inspiration.

A hardcover book laying on a wooden floor, called “Art Direction for the Web”
Andy’s new book explores 100 years of art direction and how we can use this knowledge and the newest web technologies to create better digital products.

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. Free for Smashing Members.

Hardcover

$29 $39Get the Print (incl. eBook)

Printed, quality hardcover.
Shipping starts mid-April.

Technical Details

  • 344 pages, 14 × 21 cm (5.5 × 8.25 inches)
  • ISBN: 978-3-945749-76-0 (print)
  • Quality hardcover with stitched binding and a ribbon page marker.
  • The eBook is available in PDF, ePUB, and Amazon Kindle.
  • Free worldwide airmail shipping from Germany. Starting mid-April. (See delivery times.)
  • Available as printed, quality hardcover and eBook.

Smashing Editorial
(cm, bl, il)

Source: Smashing Magazine, Meet “Art Direction For The Web,” A New Smashing Book By Andy Clarke

How To Find And Make The Most Of The Unplanned User Journey

dreamt up by webguru in Uncategorized | Comments Off on How To Find And Make The Most Of The Unplanned User Journey

How To Find And Make The Most Of The Unplanned User Journey

How To Find And Make The Most Of The Unplanned User Journey

Ben Christine



Fallbacks can be defined as an alternative plan that may be used, and in the context of user journeys, they would be an alternative path to the expected or planned user behavior. So, when we think about our sites and apps, we build them with a particular plan in mind to achieve a particular goal by taking a particular route and to experience a particular experience.

If you are experienced in building products, you will know that users have a tendency to do things you didn’t plan for, and that’s alright because we can’t possibly understand all of their needs and intention. This is why fallbacks are so valuable.

Over the years, we have tried and tested many tools at Venture Harbour to help us get better feedback loops. Our products have needs which require different types of tools to reveal all kinds of insights. From heatmaps and visitor recording tools (like Hotjar and Fullstory) to analytics platforms like Google Analytics and Amplitude.

These tools are great and help us accomplish a lot, but they only go so far. We now need to get deeper insights that can only be achieved by implementing fallbacks to your user journeys.

To start, we need to be thinking about what alternative behaviors or routes our users might experience that we didn’t see coming. Here are three common fallbacks:

  • Dead Ends
    The user’s journey has come to an end, and there are no clear or onward steps. The goal would be to offer the next steps to the user and track those choices.
  • Grey Areas
    The user is interacting with your site or app, but we have little or no understanding of what is going on, or why they are doing what they are doing. The goal is to shed light on those areas to learn about what might be happening.
  • Errors
    The user is presented with an error message that gives insufficient context. The goal here is to offer context by interacting with the user to gain feedback from them.

Let’s dive into some examples from the wild in which feedback loops are missing from popular fallbacks. Then, I will follow up with ideas of how that feedback loop might look and work in those fallbacks.

Note: Just so you know, these are mockups based on real-world scenarios.

Turning Dead Ends Into Feedback Goldmines

Dead ends in a user flow are common. One way of discovering them is by analyzing your Exit Pages in your analytics tracking tool, like Google Analytics. This will highlight common exit pages on your site or app and give you a starting point to start investigating.


Google Analytics dashboard showing Exit Pages
Google Analytics is a great free tool to get some deep insights into your sites. (Large preview)

Now we need to be asking the simple question:

“Why are users leaving at these points?”

Take a look at the actual pages these exits are happening on and start considering at what point in the process of using your app or site the user is at when they hit this dead end. Are they just getting started, are they mid-flow, or are they close to achieving a particular goal in the journey? These considerations will help you paint a better picture of the user’s situation and possible reasons.

A very common dead end is with search when you are searching for something and you are shown that “No Results” page. Businesses are starting to really invest in these pages, and you will find that you would normally be shown some sort of a result — especially on e-commerce sites.

When searching for anything, we should at least return some sort of result. When we don’t have an actual result to show, then we need to creatively think about how to capitalize on these scenarios. This can take the form of a question or a common action that could have possibly been the intention of the user’s search in the first place.

Below is an example of a dead-end search:


Generic dead-end search results page
Dead-end search results are very common in many user journeys. (Large preview)

In this case, what can we find out from the user that we don’t already know to help us improve their experience and help us learn? What questions could we be asking them? What could we provide the user at this point, now that they haven’t found what they are after?

This particular scenario is interesting, and I thought of a couple of ideas on how to get information from the user to help us improve our app and provide them with the next step.

As shown below, we start off by telling the user that what they are looking for isn’t available now, but could be in the future. In return, we can get some feedback on what they had hoped to do or find instead:


Generic dead-end search results page with smart next steps
We can easily guide users out of dead ends with smart next steps. (Large preview)

We could then provide the user with a next step by offering them a filter that will help them find an alternative and subsequently inform us of what category of extension they were searching for, which we could then review and work out what categories we should be investing time in. With this information collated from the user, we can start to build up a knowledge base of what users might expect in these scenarios and begin to suggest smarter categories and suggestions to the end user in the future. That way, we can provide them with a much better user experience.

Example: Natural Dead Ends

Dead ends can also happen as a natural order of your site or app. There might be scenarios in which users are met with a page that lacks content and no onward step due to the system not having had time to process data. Or maybe the data has not been created yet, as illustrated below:


Generic dead end processing pages
What might seem to be the nature of an app can be a loss to the user. (Large preview)

So now what we want to do is make sure of two things:

  1. The user should understand why and what’s going on;
  2. Engage the user while they wait for the data.

Below is a friendly, valuable and useful fallback for both the user and for ourselves. The user is offered notifications to be sent to their email address — along with a month’s subscription free of charge! In return, we get to inform users when to come back to the app: happy users with a free month, and extra data on the users themselves. It’s always best when everyone wins!


Generic engaging processing page
Take every opportunity to engage and guide your users. (Large preview)

Over time, we can start to understand if the majority of users do or do not want to have certain features, and whether they prefer to start turning them on by default or removing them completely. Also, information from surveys can help grow our understanding of users’ expectations of the app, and help us shape it to fit users’ needs.

Recommended reading: How To Improve Your Design Process With Data-Based Personas

Convert Grey Areas Into Insights

What I mean by ‘grey areas’ are areas that lack insight and clarity on what users might be doing or why users are making particular decisions. These are normally areas that aren’t of high priority to the site/app but are part of the key user journey. The more data we have through the user journey, the better we can understand the whats, whys and hows of users’ actions.

Example: Understanding Your “Other”

It is quite common practice to label something as ‘other’ when there isn’t a clear fit. Below is an example of this kind of UI, and as we analyze this scenario, it is clear that the only insight we have is that we know that the user is labeling this as a ‘other’ type, and that leaves us with little understanding of what it is or what they intended it to be.


Generic dropdown with other option
The “Other” option is too common. (Large preview)

Of course, we could investigate a little more about what is being labelled as ‘other’ and find some common lines, but we wouldn’t know what the user might have intended by labelling it that.

So, how can we change this scenario from being just another ‘other’ answer to something that is informative and useful to both user and us? An approach I would suggest is a little more complex but will make the experience a lot more engaging.

We want to allow users to either choose their option by clicking on the option that they know they want or by searching for what they think they are looking for. Why is this helpful? Well, when it comes to naming options, we might name them in quite a different way compared to how the user might have in mind. But if we still can’t offer the user an answer they’re looking for, then we should start a conversation with them by showing them a question and a text box, and ask them what it is that they are looking for and why. (Perhaps also add a line that we will follow up with them and find a solution to their needs.)


Generic dropdown with smart search and feedback tool
Take away the “Other” option and replace it with smart feedback loops. (Large preview)

As we start to find common threads in the searches and responses we get, we can start to adjust the search to serve the user the option the most likely need, and we can also build new features as demand increases, too.

Another grey area below is one example of myself using an instant search while looking for some keywords that are not associated with anything I have in my account.


Generic instant search grey area
Instant searches are common these days, and they can be so undervalued. (Large preview)

From this experience, I asked myself the following questions:

  • What’s the takeaway from this scenario?
  • Can I find out how common this scenario is?
  • What is the user trying to achieve?
  • How can we help them get to where they want?

Below is the ‘No Results’ results window with a feedback loop to help us learn about the users’ needs. The search results are populated in a way which can bring insights into the users’ needs, and provide us with at least some clarity into what exactly it is that they are searching for with the ‘search in’ option.


Generic instant search with feedback and common call-to-actions
Adding interesting next steps and common call-to-actions will give users a sense of what to do next.(Large preview)

A few ways to engage your users would be:

  • Offer common actions on your site or app for the user to create the content that they are looking for;
  • Provide the option of them receiving a notification once the thing they had been looking for becomes available (this is a nice way to bring the user back at a later stage);
  • Allow them to give straightforward feedback because sometimes talking to someone will solve everything (and a conversation is the best source of feedback).

We can learn and get smarter with the results we get from this feedback loop, as long as we start providing smarter category suggestiony to our users. Over time, personalized habits can be learned for individual users and we can start to suggest options that are more likely to resonate with that user because of their past actions and searches.

Make Your Errors Messages A Strength

Whether we choose to accept it or not, errors do happen. When they happen, we should know about them, but sometimes we can cater for errors in a generic way which doesn’t help us or the user.

An example of this is when the user could be interacting with your site or app where they are saving some sort of data to your system, but for some reason, the connection is lost and the process fails. There is little to diagnose what is going on or what has happened but we know there was an error. Standard procedure is to inform the user that an error has occurred and possibly to try again.


Sometimes we have an incline of what’s happened, giving the user a few options can help us learn what’s a common issue.
No one wants to see error messages, especially ones that give us little guidance. (Large preview)

When we know little about the issue on our end, or that it could have been caused by a handful of things, then this is a great opportunity to get back some feedback from the user about what’s going on. I think this is the most simple fallback to implement, but one that I see the least of.

A typical scenario would be when something is being automatically saved while you are using the site or app. This is a growing scenario especially with mobile connections; understanding that your site is used by users on-the-go or on a mobile connection is a great insight, and we should try to gather that information as much as possible.


Generic website/app error message with single feedback loop
Adding character will encourage users to give feedback. (Large preview)

Or you could make it even simpler for the user. If you already have an idea of what the issue may be and would like to get a better idea of how often it has happened to the user, then serve them with options but still allow them to tell you what they’ve experienced. Once you have that information, you can then start investing your time in possible new features to help resolve those given issues and hopefully find yourself not having to show an error message at all.


Sometimes we have an incline of what’s happened, so giving the user a few options can help us learn what’s a common issue
No one wants to see error messages, especially ones that give us little guidance. (Large preview)

Something important to remember is that a fallback doesn’t have to be a permanent feature, but it can be a temporary one to help you figure out what to ask or what to build next. It requires a little bit of investment and time to collect data, but these can be seen as test running in areas of your user journey that you have never looked into before, and will only add to the understanding of your users and their needs.

With all of this said, fallbacks in an ideal world would never exist. If you are working on a brand new project or reworking your user flow, set yourself the challenge of creating a flow where a user would never encounter scenarios like these. Consider how to prevent a user from ever searching for something that doesn’t exist. Track and analyze all areas of your user journey to understand each of their steps and formulate ways where an error message would never be shown.

Recommended reading: A/B Testing For Mobile-First Experiences

Where Are Fallbacks Costing You?

Fallbacks will look different depending on your product, as you will be asking different questions and offering different options, but they will all play a valuable role in exposing yourself and the users to better options and understanding.

What Next?

Why not take a few hours to look at your user journeys and document where the grey areas are, as well as look for dead ends and error messages that (still) exist in your product.

Here are some guides to get you started:

  • Dead Ends
    Start exploring your analytics and — if you aren’t already — get comfortable in there. The exit pages are normally tracked by default and will provide you with great insights to what stage your users are leaving the site. Think about where your users will be in their journey and consider the kind of needs they might have at those points.
  • Grey Areas
    If you haven’t already, map out your user journey and start highlighting areas that you might have questions about. Consider the points in which your users are making decisions and think about the options you are giving them. Remember the “Other” option and think about where there might be any that are quite generic. Start tracking those options to see how often they are chosen, then start exploring the ways to get feedback from the users on what they want.
  • Errors
    You can quickly work out where an error might be shown on your app. With a site/app that is saving, updating and working in real time, errors are possibly a key part to your system. Start by figuring out which area is most used, which could result in where errors are most frequently seen. Invest in the error messages there and start gathering feedback on why those are being shown — both from your system but also the user. I would also recommend creating a default error message with a feedback loop that would be used across the site for any new feature so that you will start learning from those errors from day one.

Happy fallback building!

Smashing Editorial
(cc, ra, il)

Source: Smashing Magazine, How To Find And Make The Most Of The Unplanned User Journey

SVG Circle Decomposition To Paths

dreamt up by webguru in Uncategorized | Comments Off on SVG Circle Decomposition To Paths

SVG Circle Decomposition To Paths

SVG Circle Decomposition To Paths

Bryan Rasmussen



This article starts with a confession: I like to hand-code SVG. It’s not always the case but often enough it could seem peculiar to people who do not share my predilection. There are a good number of benefits in being able to write SVG by hand, such as optimizing SVGs in ways a tool can’t (turning a path into a simpler path or shape), or by simply understanding how libraries like D3 or Greensock work.

With that said, I’d like to look more closely at circular shapes in SVG and things we can do with them when we move past a basic circle. Why circles? Well, I love circles. They’re my favorite shape.

First off (hopefully you’ve seen a basic circle in SVG before), here’s a pen that shows one:

See the Pen circle by Bryan Rasmussen.

A lot of things can be done with a circle: it can be animated and it can have different colors applied to it. Still, there are two very nice things that you cannot have a circle do in SVG 1.1: You cannot make another graphical element move along the circle’s path (using the animateMotion element) and you cannot have shape a text along a circle’s path (this will only be allowed after SVG 2.0 is released).

Turning Our Circle Into A Path

There is a little online tool that can help you create paths out of circles (you can try it out here), but we’re going to do be creating everything from scratch so we can find out what’s really going on behind the scenes.

To make a circular path, we’re going to actually make two arcs, i.e. semicircles that complete the circle in one path. As you’ve probably noticed in the SVG above, the attributes CX, CY, and R respectively define where the circle is drawn along the X and Y axis, while R defines the radius of the circle. The CX and CY create the center of the circle, so the circle is drawn around that point.

Replicating that circle could look like this:

<path
    d="
      M (CX - R), CY
      a R,R 0 1,0 (R * 2),0
      a R,R 0 1,0 -(R * 2),0
    "
/>

Note that CX is the same as the cx attribute of the circle; the same goes for CY and the cy attribute of the circle, as well as R and the r attribute of the circle. The small a character is used to define a segment of an elliptical arc. You can use an optional Z (or z) to close the path.

The lowercase letter a denotes the beginning of an elliptical arc drawn relatively to the current position — or in our specific case:

<path
  d="
    M 25, 50
    a 25,25 0 1,1 50,0
    a 25,25 0 1,1 -50,0
  "
/>

You can see the magic happening in this pen:

See the Pen circle from path by Bryan Rasmussen.

Hidden underneath the path is a circle with a red fill. As you play around with values of the path, you’ll see that circle as long as the path totally covers the circle (the path itself is a circle of the same size), and we’ll know that we’re doing things right.

One thing you should also know is that as long as you are drawing relative arcs, you don’t need to repeat the a command for each arc you draw. When your first 7 inputs are done for your arc, the second 7 inputs will be taken for the next arc.

You can try this out with the pen above by removing the second a in the path:

a 25,25 0 1,1 50,0

25,25 0 1,1 -50,0

This may look the same, but I prefer to leave it in until I am ready to finish a drawing, and this also helps me to keep track of where I am.

How This Path Works

First, we move to an absolutely positioned X,Y coordinate in the image. It does not draw anything there — it just moves there. Remember that for a circle element CX, CY denotes the center of the circle; but as it happens in the elliptical arc, the true CX and CY of the arc will be calculated from the other properties of that arc.

In other words, if we want our CX to be at 50 and our radius is 25, then we need to move to 50 - 25 (if we are drawing from left to right, of course). This means that our first arc is drawn from 25 X, 50 Y which results to our first arc being 25,25 0 1,0 50,0.

Let’s break down what the value 25,25 0 1,0 50,0 of our arc actually means:

  • 25: The relative X radius of the arc;
  • 25: The relative Y radius of the arc;
  • 0 1,0: I’m not going to talk about the three middle values (rotation, large-arc-flag, and the sweep-flag properties) because they are not very important in the context of the current example as long as they are the same for both arcs;
  • 50: The ending X coordinate (relative) of the arc;
  • 0: The ending Y coordinate (relative) of the arc.

The second arc is a 25,25 0 1,0 -50,0. Keep in mind that this arc will start drawing from wherever the last arc stopped drawing. Of course, the X and Y radius are the same (25), but the ending X coordinate is -50 of where the current one is.

Obviously this circle could have been drawn in many different ways. This process of turning a circle into a path is known as decomposition. In the SVG 2 spec decomposition of a circle will be done with 4 arcs, however, the method it recommends is not possible to use yet, as it currently depends on a feature named segment-completing close path which has not yet been specified.

In order to show you that we can draw the circle in a lot of ways, I have prepared a little pen with various examples:

See the Pen all circles by Bryan Rasmussen.

If you take a closer look, you’ll see our original circle along with five different examples of how to draw paths on top of that circle. Each path has a child desc element describing the use of CX, CY and R values to build the circle. The first example is the one we discussed here while three others use variations that should be comprehensible from reading the code; the last examples uses four semicircular arcs instead of two, replicating somewhat the process described in the SVG 2 spec linked above.

The circles are layered on top of each other using SVG’s natural z-indexing of placing elements that come later in the markup on top of the ones that come earlier.

If you click on the circular paths in the pen, the first click will print out how the path is structured to the console and add a class to the element so that you will see the stroke color of how the circle is drawn (you can see that the first circle is drawn with a starting wedge from the stroke). The second click will remove the circle so you have the ability to interact with the circle below.

Each circle has a different fill color; the actual circle element is yellow and will say “You clicked on the circle” to the console whenever it is clicked on. You can also, of course, simply read the code as the desc elements are quite straightforward.

Going From A Path To A Circle

I suppose you’ve noticed that while there are many different ways to draw the circle, the paths used still look pretty similar. Often — especially in SVGs output from a drawing program — circles will be represented by paths. This is probably due to optimization of the graphics program code; once you have the code to draw a path you can draw anything, so just use that. This can lead to somewhat bloated SVGs that are hard to reason about.

Recommended reading: “Tips For Creating And Exporting Better SVGs For The Web” by Sara Soueidan

Let’s take the following SVG from Wikipedia as an example. When you look at the code for that file, you will see that it has a lot of editor cruft once you’ve run it through Jake Archibald’s SVGOMG! (which you can read more about here). You’ll end up with something like the following file which has been pretty optimized, but the circles in the document are still rendered as paths:

See the Pen Wikipedia Screw Head Clutch Type A by Bryan Rasmussen.

So, let’s see if we can figure out what those circles should be if they were actual circle elements given what we know about how paths work. The first path in the document is obviously not a circle while the next two are (showing just the d attribute):

M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z

So remembering that the second a can be left out, let’s rewrite these to make a little more sense. (The first path is the big circle.)

M39 20
a19 19 0 1 1-38 0
a19 19 0 1 1 38 0z

Those arcs are then obviously the following:

aR R 0 1 1 - (R * 2) 0
aR R 0 1 1 (R * 2) 0

This means that our circle radius is 19, but what are our CX and CY values? I think our M39 is actually CX + R, which means that CX is 20 and CY is 20 too.

Let’s say you add in a circle after all the paths like this:

<circle
 fill="none"
 stroke-width="1.99975"
 stroke="red"
 r="19"
 cx="20"
 cy="20"
/>

You will see that is correct, and that the red stroked circle covers exactly the large circle. The second circle path reformulated looks like this:

M25 20
a5 5 0 1 1-10 0 
5 5 0 1 1 10 0z

Obviously, the radius is 5, and I bet our CX and CY values are the same as before: - 20.

Note: If CX = 20, then CX + R = 25. The circle is sitting inside the bigger one at the center, so obviously it should have the same CX and CY values.

Add the following circle at the end of the paths:

<circle
 fill="yellow"
 r="5"
 cx="20"
 cy="20"
/>

You can now see that this is correct by taking a look at the following pen:

See the Pen Wikipedia Screw Head Clutch Type A_ with example circles by Bryan Rasmussen.

Now that we know what the circles should be, we can remove those unneeded paths and actually create the circles — as you can see here:

See the Pen Wikipedia Screw Head Clutch Type A optimized by Bryan Rasmussen.

Using Our Circular Path For Wrapping Text

So now that we have our circles in paths, we can wrap text on those paths. Below is a pen with the same paths as our previous “All Circles” pen, but with text wrapped on the path. Whenever you click on a path, that path will be deleted and the text will be wrapped on the next available path, like so:

See the Pen all circles wrapped Text by Bryan Rasmussen.

Looking at the different paths, you’ll see tiny differences between each one (more on that in a bit), but first there is a little cross-browser incompatibility to be seen — especially noticeable in the first path:

Firefox Developer
Chrome
Microsoft Edge

The reason why the starting “S” of “Smashing” is sitting at that funny angle in the Firefox solution is that it is where we actually started drawing our path at (due to the v-R command we used). This is more obvious in the Chrome version where you can clearly see the first pie-shaped wedge of our circle that we drew:

Chrome does not follow all the wedges, so this is the result when you change the text to be “Smashing Magazine”.

The reason is that Chrome has a bug regarding inheritance of the textLength attribute declared on the parent text element. If you want them both to look the same, put the textLength attribute on the textPath element as well as the text. Why? Because it turns out that Firefox Developer has the same bug if the textLength attribute is not specified on the text element (this has been the case for some years now).

Microsoft Edge has a totally different bug; it can’t handle whitespace in between the Text and the child TextPath element. Once you have removed whitespace, and put the textLength attribute on both the text and textPath elements, they will all look relatively the same (with small variations due to differences in default fonts and so forth). So, three different bugs on three different browsers — this is why people often prefer to work with libraries!

The following pen shows how the problems can be fixed:

See the Pen all circles wrapped Text fixed TextLength by Bryan Rasmussen.

I’ve also removed the various fill colors because it makes it easier to see the text wrapping. Removing the fill colors means that my little function to allow you to cycle through the paths and see how they look won’t work unless I add a pointer-events="all" attribute, so I’ve added those as well.

Note: You can read more about the reasons for that in “Managing SVG Interaction With The Pointer Events Property” explained by Tiffany B. Brown.

We’ve already discussed the wrapping of the multiarc path, so let’s now look at the others. Since we have one path we are wrapping on, the text will always move in the same direction.

Image Path Explanation
M CX, CY
a R, R 0 1,0 -(R * 2), 0
a R, R 0 1,0 R * 2, 0
and uses the translate function to move +R on the X axis.
The starting position for our textPath (since we have not specified it in any way) is determined by our first ending arc -(R * 2), given the radius that the arc itself has.
M (CX + R), CY
a R,R 0 1,0 -(R * 2),0
a R,R 0 1,0 (R * 2),0
Same applies as the previous path.
M CX CY
m -R, 0
a R,R 0 1,0 (R * 2),0
a R,R 0 1,0 -(R * 2),0
Since we are ending at (R * 2 ) in our first arc, we will obviously be starting at the opposite position. In other words, this one starts where our previous two paths ended.
M (CX - R), CY
a R,R 0 1,1 (R * 2),0
a R,R 0 1,1 -(R * 2),0
This starts in the same position as the last one due to (R * 2), but it is running clockwise because we have set the sweep-flag property (marked in yellow) to 1.

We‘ve seen how to wrap text on a single path in a circle. Let’s now take a look at how we can break up that path into two paths and the benefits you can get from that.

Breaking Our Paths Into Parts

There are a lot of things you can do with the text in your path, i.e. achieving stylistic effects with tspan elements, setting the offset of the text, or animating the text. Basically, whatever you do will be constrained by the path itself. But by breaking up our multiarc paths into single arc paths, we can play around with the direction of our text, the z-indexing of different parts of our text, and achieving more complex animations.

First, we are going to want to use another SVG image to show some of the effects. I will be using the diamond from the article on pointer events which I mentioned earlier. First, let’s show what it will look like with a single path circular text laid on top of it.

Let’s assume that our circle is CX 295, CY 200, R 175. Now, following the Circular path method, we now see the following:

M (CX - R), CY
a R,R 0 1,1 (R * 2),0
a R,R 0 1,1 -(R * 2),0

See the Pen SVG Amethyst by Bryan Rasmussen.

I’m not going to talk about the path or the text size, fill or stroke color. We should all understand that by now, and be able to make it be whatever we want it to be. But by looking at the text, we can see some downsides or limitations right away:

  • The text all runs in one direction;
  • It might be nice to have some of the text go behind the amethyst, especially where it says MAGAZINE. In order to make the ‘M’ and ‘E’ line up on the circle, the ‘A’ has to be on the side lower point of the amethyst, which feels sort of unbalanced in another way. (I feel like the ‘A’ should be precisely positioned and pointing down at that point.)

If we want to fix these issues, we need to split our single path into two. In the following pen, I have separated the path into two paths, (and placed them into the defs area of the SVG for our textPaths to reference):

See the Pen SVG Amethyst two paths by Bryan Rasmussen.

Again, assuming our CX is 295, CY 200, R 175, then the two paths are in the format of the following (for the top semicircular path):

M (CX - R), CY
a R,R 0 1,1 (R * 2),0

And the following for the bottom:

M (CX + R), CY
a R,R 0 1,1 -(R * 2),0

However, we still have circular text that moves all in the same direction. To fix that for everything but Edge, all you have to do is to add the side="right" attribute to the text element that holds the ‘MAGAZINE’ textPath.

Making The Text Go Another Direction

If we want to support as many browsers as we can, we have to alter the path and not rely on the side attribute which is not fully supported. What we can do is to copy our top semicircle path, but change the sweep from 1 to 0:

Before:

M 120, 200
a 175,175 0 1,1 350,0

After:

M 120, 200
a 175,175 0 1,0 350,0

But our text is now drawn on the inner circle defined by the sweep and it won’t look so nice in different browsers. This means that we’re going to have to move the position of our path to align with the ‘S’ of ‘Smashing’, make the ending X of the path greater, and set some offset to the text. As you can see, there is also a little text difference between Firefox and the others which we can improve by increasing the textLength attribute on the text element, as well as removing whitespace from the textPath (since Firefox evidently thinks whitespace is meaningful).

The solution:

See the Pen SVG Amethyst two paths fixed by Bryan Rasmussen.

Change The Z-Index Of Part Of Our Circular Text

Finally, we want to make our text goes both in front and behind the amethyst. Well, that’s easy. Remember that SVG’s z-indexing of element is based by where they are in the markup? So if we have two elements, element 1 will be drawn behind element 2. Next, all we have to do is to move a text element up in our SVG markup so it is drawn before the amethyst.

You can see the result below in which parts of the word ‘MAGAZINE’ are hidden by the lower point of the amethyst.

See the Pen SVG Amethyst two paths z-index by Bryan Rasmussen.

If you take a look at the markup, you can see that the lower semicircle of text has been moved to be before the path that draws the amethyst.

Animating The Parts Of Our Circle

So now we have the ability to make circular text by completely controlling the directionality of the parts of our text by putting the text into two semicircles. This can, of course, also be exploited to make animations of the text. Making cross-browser SVG animations is really the subject of another article (or a lot more articles). These examples will only work in Chrome and Firefox because of using the SMIL-animations syntax instead of CSS keyframes or tools like Greensock. But it gives a good indicator of the effects you can achieve by animating the decomposed circle.

Take the following pen:

See the Pen SVG Amethyst two paths animated by Bryan Rasmussen.

Please press the ‘Rerun’ button on the codepen to see the animation in action. The two parts of our circular text begin animating at the same time, but have a different duration so they end at different times. Because we are animating the textLength attribute, we have put two animate directives under each text — one for the text element (so Firefox will work) and one for the textpath element (so Chrome will work).

Conclusion

In this article, we’ve seen how to turn a circle into a path and back again, in order to better understand when we need to optimize a path and when not. We’ve seen how turning the circle into a path frees us up to placing the text on the circular path, but also how to further split the circular path into semicircles and gain fuller control over directionality and animation of the component parts of our circular text.

Further Reading on SmashingMag:

Smashing Editorial
(dm, ra, yk, il)

Source: Smashing Magazine, SVG Circle Decomposition To Paths

How To Make A Speech Synthesis Editor

dreamt up by webguru in Uncategorized | Comments Off on How To Make A Speech Synthesis Editor

How To Make A Speech Synthesis Editor

How To Make A Speech Synthesis Editor

Knut Melvær



When Steve Jobs unveiled the Macintosh in 1984, it said “Hello” to us from the stage. Even at that point, speech synthesis wasn’t really a new technology: Bell Labs developed the vocoder as early as in the late 30s, and the concept of a voice assistant computer made it into people’s awareness when Stanley Kubrick made the vocoder the voice of HAL9000 in 2001: A Space Odyssey (1968).

It wasn’t before the introduction of Apple’s Siri, Amazon Echo, and Google Assistant in the mid 2015s that voice interfaces actually found their way into a broader public’s homes, wrists, and pockets. We’re still in an adoption phase, but it seems that these voice assistants are here to stay.

In other words, the web isn’t just passive text on a screen anymore. Web editors and UX designers have to get accustomed to making content and services that should be spoken out loud.

We’re already moving fast towards using content management systems that let us work with our content headlessly and through APIs. The final piece is to make editorial interfaces that make it easier to tailor content for voice. So let’s do just that!

What Is SSML

While web browsers use W3C’s specification for HyperText Markup Language (HTML) to visually render documents, most voice assistants use Speech Synthesis Markup Language (SSML) when generating speech.

A minimal example using the root element <speak>, and the paragraph (<p>) and sentence (<s>) tags:

<speak>
  <p>
    <s>This is the first sentence of the paragraph.</s>
    <s>Here’s another sentence.</s>
  </p>
</speak>
Press play to listen to the snippet:

Where SSML gets existing is when we introduce tags for <emphasis> and <prosody> (pitch):

<speak>
  <p>
    <s>Put some <emphasis strength="strong">extra weight on these words</emphasis></s>
    <s>And say <prosody pitch="high" rate="fast">this a bit higher and faster</prosody>!</s>
  </p>
</speak>
Press play to listen to the snippet:

SSML has more features, but this is enough to get a feel for the basics. Now, let’s take a closer look at the editor that we will use to make the speech synthesis editing interface.

The Editor For Portable Text

To make this editor, we’ll use the editor for Portable Text that features in Sanity.io. Portable Text is a JSON specification for rich text editing, that can be serialized into any markup language, such as SSML. This means you can easily use the same text snippet in multiple places using different markup languages.


Sanity.io’s default editor for Portable Text
Sanity.io’s default editor for Portable Text (Large preview)

Installing Sanity

Sanity.io is a platform for structured content that comes with an open-source editing environment built with React.js. It takes two minutes to get it all up and running.

Type npm i -g @sanity/cli && sanity init into your terminal, and follow the instructions. Choose “empty”, when you’re prompted for a project template.

If you don’t want to follow this tutorial and make this editor from scratch, you can also clone this tutorial’s code and follow the instructions in README.md.

When the editor is downloaded, you run sanity start in the project folder to start it up. It will start a development server that use Hot Module Reloading to update changes as you edit its files.

How To Configure Schemas In Sanity Studio

Creating The Editor Files

We’ll start by making a folder called ssml-editor in the /schemas folder. In that folder, we’ll put some empty files:

/ssml-tutorial/schemas/ssml-editor
                        ├── alias.js
                        ├── emphasis.js
                        ├── annotations.js
                        ├── preview.js
                        ├── prosody.js
                        ├── sayAs.js
                        ├── blocksToSSML.js
                        ├── speech.js
                        ├── SSMLeditor.css
                        └── SSMLeditor.js

Now we can add content schemas in these files. Content schemas are what defines the data structure for the rich text, and what Sanity Studio uses to generate the editorial interface. They are simple JavaScript objects that mostly require just a name and a type.

We can also add a title and a description to make a bit nicer for editors. For example, this is a schema for a simple text field for a title:

export default {
  name: 'title',
  type: 'string',
  title: 'Title',
  description: 'Titles should be short and descriptive'
}

Sanity Studio with a title field and an editor for Portable Text
The studio with our title field and the default editor (Large preview)

Portable Text is built on the idea of rich text as data. This is powerful because it lets you query your rich text, and convert it into pretty much any markup you want.

It is an array of objects called “blocks” which you can think of as the “paragraphs”. In a block, there is an array of children spans. Each block can have a style and a set of mark definitions, which describe data structures distributed on the children spans.

Sanity.io comes with an editor that can read and write to Portable Text, and is activated by placing the block type inside an array field, like this:

// speech.js
export default {
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  of: [
    { type: 'block' }
  ]
}

An array can be of multiple types. For an SSML-editor, those could be blocks for audio files, but that falls outside of the scope of this tutorial.

The last thing we want to do is to add a content type where this editor can be used. Most assistants use a simple content model of “intents” and “fulfillments”:

  • Intents
    Usually a list of strings used by the AI model to delineate what the user wants to get done.
  • Fulfillments
    This happens when an “intent” is identified. A fulfillment often is — or at least — comes with some sort of response.

So let’s make a simple content type called fulfillment that use the speech synthesis editor. Make a new file called fulfillment.js and save it in the /schema folder:

// fulfillment.js
export default {
  name: 'fulfillment',
  type: 'document',
  title: 'Fulfillment',
  of: [
    {
      name: 'title',
      type: 'string',
      title: 'Title',
      description: 'Titles should be short and descriptive'
    },
    {
      name: 'response',
      type: 'speech'
    }
  ]
}

Save the file, and open schema.js. Add it to your studio like this:

// schema.js
import createSchema from 'part:@sanity/base/schema-creator'
import schemaTypes from 'all:part:@sanity/base/schema-type'
import fullfillment from './fullfillment'
import speech from './speech'

export default createSchema({
  name: 'default',
  types: schemaTypes.concat([
    fullfillment,
    speech,
  ])
})

If you now run sanity start in your command line interface within the project’s root folder, the studio will start up locally, and you’ll be able to add entries for fulfillments. You can keep the studio running while we go on, as it will auto-reload with new changes when you save the files.

Adding SSML To The Editor

By default, the block type will give you a standard editor for visually oriented rich text with heading styles, decorator styles for emphasis and strong, annotations for links, and lists. Now we want to override those with the audial concepts found in SSML.

We begin with defining the different content structures, with helpful descriptions for the editors, that we will add to the block in SSMLeditorSchema.js as configurations for annotations. Those are “emphasis”, “alias”, “prosody”, and “say as”.

Emphasis

We begin with “emphasis”, which controls how much weight is put on the marked text. We define it as a string with a list of predefined values that the user can choose from:

// emphasis.js
export default {
  name: 'emphasis',
  type: 'object',
  title: 'Emphasis',
  description:
    'The strength of the emphasis put on the contained text',
  fields: [
    {
      name: 'level',
      type: 'string',
      options: {
        list: [
          { value: 'strong', title: 'Strong' },
          { value: 'moderate', title: 'Moderate' },
          { value: 'none', title: 'None' },
          { value: 'reduced', title: 'Reduced' }
        ]
      }
    }
  ]
}

Alias

Sometimes the written and the spoken term differ. For instance, you want to use the abbreviation of a phrase in a written text, but have the whole phrase read aloud. For example:

<s>This is a <sub alias="Speech Synthesis Markup Language">SSML</sub> tutorial</s>
Press play to listen to the snippet:

The input field for the alias is a simple string:

// alias.js
export default {
  name: 'alias',
  type: 'object',
  title: 'Alias (sub)',
  description:
    'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.',
  fields: [
    {
      name: 'text',
      type: 'string',
      title: 'Replacement text',
    }
  ]
}

Prosody

With the prosody property we can control different aspects how text should be spoken, like pitch, rate, and volume. The markup for this can look like this:

<s>Say this with an <prosody pitch="x-low">extra low pitch</prosody>, and this <prosody rate="fast" volume="loud">loudly with a fast rate</prosody></s>
Press play to listen to the snippet:

This input will have three fields with predefined string options:

// prosody.js
export default {
  name: 'prosody',
  type: 'object',
  title: 'Prosody',
  description: 'Control of the pitch, speaking rate, and volume',
  fields: [
    {
      name: 'pitch',
      type: 'string',
      title: 'Pitch',
      description: 'The baseline pitch for the contained text',
      options: {
        list: [
          { value: 'x-low', title: 'Extra low' },
          { value: 'low', title: 'Low' },
          { value: 'medium', title: 'Medium' },
          { value: 'high', title: 'High' },
          { value: 'x-high', title: 'Extra high' },
          { value: 'default', title: 'Default' }
        ]
      }
    },
    {
      name: 'rate',
      type: 'string',
      title: 'Rate',
      description:
        'A change in the speaking rate for the contained text',
      options: {
        list: [
          { value: 'x-slow', title: 'Extra slow' },
          { value: 'slow', title: 'Slow' },
          { value: 'medium', title: 'Medium' },
          { value: 'fast', title: 'Fast' },
          { value: 'x-fast', title: 'Extra fast' },
          { value: 'default', title: 'Default' }
        ]
      }
    },
    {
      name: 'volume',
      type: 'string',
      title: 'Volume',
      description: 'The volume for the contained text.',
      options: {
        list: [
          { value: 'silent', title: 'Silent' },
          { value: 'x-soft', title: 'Extra soft' },
          { value: 'medium', title: 'Medium' },
          { value: 'loud', title: 'Loud' },
          { value: 'x-loud', title: 'Extra loud' },
          { value: 'default', title: 'Default' }
        ]
      }
    }
  ]
}

Say As

The last one we want to include is <say-as>. This tag lets us exercise a bit more control over how certain information is pronounced. We can even use it to bleep out words if you need to redact something in voice interfaces. That’s @!%&© useful!

<s>Do I have to <say-as interpret-as="expletive">frakking</say-as> <say-as interpret-as="verbatim">spell</say-as> it out for you!?</s>
Press play to listen to the snippet:

// sayAs.js
export default {
  name: 'sayAs',
  type: 'object',
  title: 'Say as...',
  description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering
  the contained text.',
  fields: [
    {
      name: 'interpretAs',
      type: 'string',
      title: 'Interpret as...',
      options: {
        list: [
          { value: 'cardinal', title: 'Cardinal numbers' },
          {
            value: 'ordinal',
            title: 'Ordinal numbers (1st, 2nd, 3th...)'
          },
          { value: 'characters', title: 'Spell out characters' },
          { value: 'fraction', title: 'Say numbers as fractions' },
          { value: 'expletive', title: 'Blip out this word' },
          {
            value: 'unit',
            title: 'Adapt unit to singular or plural'
          },
          {
            value: 'verbatim',
            title: 'Spell out letter by letter (verbatim)'
          },
          { value: 'date', title: 'Say as a date' },
          { value: 'telephone', title: 'Say as a telephone number' }
        ]
      }
    },
    {
      name: 'date',
      type: 'object',
      title: 'Date',
      fields: [
        {
          name: 'format',
          type: 'string',
          description: 'The format attribute is a sequence of date field character codes. Supported field character codes in format are {y, m, d} for year, month, and day (of the month) respectively. If the field code appears once for year, month, or day then the number of digits expected are 4, 2, and 2 respectively. If the field code is repeated then the number of expected digits is the number of times the code is repeated. Fields in the date text may be separated by punctuation and/or spaces.'
        },
        {
          name: 'detail',
          type: 'number',
          validation: Rule =>
            Rule.required()
              .min(0)
              .max(2),
          description: 'The detail attribute controls the spoken form of the date. For detail='1' only the day fields and one of month or year fields are required, although both may be supplied'
        }
      ]
    }
  ]
}

Now we can import these in an annotations.js file, which makes things a bit tidier.

// annotations.js
export {default as alias} from './alias'
export {default as emphasis} from './emphasis'
export {default as prosody} from './prosody'
export {default as sayAs} from './sayAs'

Now we can import these annotation types into our main schemas:

// schema.js
import createSchema from "part:@sanity/base/schema-creator"
import schemaTypes from "all:part:@sanity/base/schema-type"
import fulfillment from './fulfillment'
import speech from './ssml-editor/speech'
import {
  alias,
  emphasis,
  prosody,
  sayAs
} from './annotations'

export default createSchema({
  name: "default",
  types: schemaTypes.concat([
    fulfillment,
    speech,
    alias,
    emphasis,
    prosody,
    sayAs
  ])
})

Finally, we can now add these to the editor like this:

// speech.js
export default {
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  of: [
    {
      type: 'block',
      styles: [],
      lists: [],
      marks: {
        decorators: [],
        annotations: [
          {type: 'alias'},
          {type: 'emphasis'},
          {type: 'prosody'},
          {type: 'sayAs'}
        ]
      }
    }
  ]
}

Notice that we also added empty arrays to styles, and decorators. This disables the default styles and decorators (like bold and emphasis) since they don’t make that much sense in this specific case.

Customizing The Look And Feel

Now we have the functionality in place, but since we haven’t specified any icons, each annotation will use the default icon, which makes the editor hard to actually use for authors. So let’s fix that!

With the editor for Portable Text it’s possible to inject React components both for the icons and for how the marked text should be rendered. Here, we’ll just let some emoji do the work for us, but you could obviously go far with this, making them dynamic and so on. For prosody we’ll even make the icon change depending on the volume selected. Note that I omitted the fields in these snippets for brevity, you shouldn’t remove them in your local files.

// alias.js
import React from 'react'

export default {
  name: 'alias',
  type: 'object',
  title: 'Alias (sub)',
  description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.',
  fields: [
    /* all the fields */
  ],
  blockEditor: {
    icon: () => '🔤',
    render: ({ children }) => <span>{children} 🔤</span>,
  },
};
// emphasis.js
import React from 'react'

export default {
  name: 'emphasis',
  type: 'object',
  title: 'Emphasis',
  description: 'The strength of the emphasis put on the contained text',
  fields: [
    /* all the fields */
  ],
  blockEditor: {
    icon: () => '🗯',
    render: ({ children }) => <span>{children} 🗯</span>,
  },
};

// prosody.js
import React from 'react'

export default {
  name: 'prosody',
  type: 'object',
  title: 'Prosody',
  description: 'Control of the pitch, speaking rate, and volume',
  fields: [
    /* all the fields */
  ],
  blockEditor: {
    icon: () => '🔊',
    render: ({ children, volume }) => (
      <span>
        {children} {['x-loud', 'loud'].includes(volume) ? '🔊' : '🔈'}
      </span>
    ),
  },
};
// sayAs.js
import React from 'react'

export default {
  name: 'sayAs',
  type: 'object',
  title: 'Say as...',
  description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.',
  fields: [
    /* all the fields */
  ],
  blockEditor: {
    icon: () => '🗣',
    render: props => <span>{props.children} 🗣</span>,
  },
};


The customized SSML editor
The editor with our custom SSML marks (Large preview)

Now you have an editor for editing text that can be used by voice assistants. But wouldn’t it be kinda useful if editors also could preview how the text actually will sound like?

Adding A Preview Button Using Google’s Text-to-Speech

Native speech synthesis support is actually on its way for browsers. But in this tutorial, we’ll use Google’s Text-to-Speech API which supports SSML. Building this preview functionality will also be a demonstration of how you serialize Portable Text into SSML in whatever service you want to use this for.

Wrapping The Editor In A React Component

We begin with opening the SSMLeditor.js file and add the following code:

// SSMLeditor.js
import React, { Fragment } from 'react';
import { BlockEditor } from 'part:@sanity/form-builder';

export default function SSMLeditor(props) {
  return (
    <Fragment>
      <BlockEditor {...props} />
    </Fragment>
  );
}

We have now wrapped the editor in our own React component. All the props it needs, including the data it contains, are passed down in real-time. To actually use this component, you have to import it into your speech.js file:

// speech.js
import React from 'react'
import SSMLeditor from './SSMLeditor.js'

export default {
  name: 'speech',
  type: 'array',
  title: 'SSML Editor',
  inputComponent: SSMLeditor,
  of: [
    {
      type: 'block',
      styles: [],
      lists: [],
      marks: {
        decorators: [],
        annotations: [
          { type: 'alias' },
          { type: 'emphasis' },
          { type: 'prosody' },
          { type: 'sayAs' },
        ],
      },
    },
  ],
}

When you save this and the studio reloads, it should look pretty much exactly the same, but that’s because we haven’t started tweaking the editor yet.

Convert Portable Text To SSML

The editor will save the content as Portable Text, an array of objects in JSON that makes it easy to convert rich text into whatever format you need it to be. When you convert Portable Text into another syntax or format, we call that “serialization”. Hence, “serializers” are the recipes for how the rich text should be converted. In this section, we will add serializers for speech synthesis.

You have already made the blocksToSSML.js file. Now we’ll need to add our first dependency. Begin by running the terminal command npm init -y inside the ssml-editor folder. This will add a package.json where the editor’s dependencies will be listed.

Once that’s done, you can run npm install @sanity/block-content-to-html to get a library that makes it easier to serialize Portable Text. We’re using the HTML-library because SSML has the same XML syntax with tags and attributes.

This is a bunch of code, so do feel free to copy-paste it. I’ll explain the pattern right below the snippet:

// blocksToSSML.js
import blocksToHTML, { h } from '@sanity/block-content-to-html'

const serializers = {
  marks: {
    prosody: ({ children, mark: { rate, pitch, volume } }) =>
      h('prosody', { attrs: { rate, pitch, volume } }, children),
    alias: ({ children, mark: { text } }) =>
      h('sub', { attrs: { alias: text } }, children),
    sayAs: ({ children, mark: { interpretAs } }) =>
      h('say-as', { attrs: { 'interpret-as': interpretAs } }, children),
    break: ({ children, mark: { time, strength } }) =>
      h('break', { attrs: { time: '${time}ms', strength } }, children),
    emphasis: ({ children, mark: { level } }) =>
      h('emphasis', { attrs: { level } }, children)
  }
}

export const blocksToSSML = blocks => blocksToHTML({ blocks, serializers })

This code will export a function that takes the array of blocks and loop through them. Whenever a block contains a mark, it will look for a serializer for the type. If you have marked some text to have emphasis, it this function from the serializers object:

emphasis: ({ children, mark: { level } }) =>
      h('emphasis', { attrs: { level } }, children)

Maybe you recognize the parameter from where we defined the schema? The h() function lets us defined an HTML element, that is, here we “cheat” and makes it return an SSML element called <emphasis>. We also give it the attribute level if that is defined, and place the children elements within it — which in most cases will be the text you have marked up with emphasis.

{
    "_type": "block",
    "_key": "f2c4cf1ab4e0",
    "style": "normal",
    "markDefs": [
        {
            "_type": "emphasis",
            "_key": "99b28ed3fa58",
            "level": "strong"
        }
    ],
    "children": [
        {
            "_type": "span",
            "_key": "f2c4cf1ab4e01",
            "text": "Say this strongly!",
            "marks": [
                "99b28ed3fa58"
            ]
        }
    ]
}

That is how the above structure in Portable Text gets serialized to this SSML:

<emphasis level="strong">Say this strongly</emphasis>

If you want support for more SSML tags, you can add more annotations in the schema, and add the annotation types to the marks section in the serializers.

Now we have a function that returns SSML markup from our marked up rich text. The last part is to make a button that lets us send this markup to a text-to-speech service.

Adding A Preview Button That Speaks Back To You

Ideally, we should have used the browser’s speech synthesis capabilities in the Web API. That way, we would have gotten away with less code and dependencies.

As of early 2019, however, native browser support for speech synthesis is still in its early stages. It looks like support for SSML is on the way, and there is proof of concepts of client-side JavaScript implementations for it.

Chances are that you are going to use this content with a voice assistant anyways. Both Google Assistant and Amazon Echo (Alexa) support SSML as responses in a fulfillment. In this tutorial, we will use Google’s text-to-speech API, which also sounds good and support several languages.

Start by obtaining an API key by signing up for Google Cloud Platform (it will be free for the first 1 million characters you process). Once you’re signed up, you can make a new API key on this page.

Now you can open your PreviewButton.js file, and add this code to it:

// PreviewButton.js
import React from 'react'
import Button from 'part:@sanity/components/buttons/default'
import { blocksToSSML } from './blocksToSSML'

// You should be careful with sharing this key
// I put it here to keep the code simple
const API_KEY = '<yourAPIkey>'
const GOOGLE_TEXT_TO_SPEECH_URL = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize?key=' + API_KEY

const speak = async blocks => {
  // Serialize blocks to SSML
  const ssml = blocksToSSML(blocks)
  // Prepare the Google Text-to-Speech configuration
  const body = JSON.stringify({
    input: { ssml },
    // Select the language code and voice name (A-F)
    voice: { languageCode: 'en-US', name: 'en-US-Wavenet-A' },
    // Use MP3 in order to play in browser
    audioConfig: { audioEncoding: 'MP3' }
  })
  // Send the SSML string to the API
  const res = await fetch(GOOGLE_TEXT_TO_SPEECH_URL, {
    method: 'POST',
    body
  }).then(res => res.json())
  // Play the returned audio with the Browser’s Audo API
  const audio = new Audio('data:audio/wav;base64,' + res.audioContent)
  audio.play()
}

export default function PreviewButton (props) {
  return <Button style={{ marginTop: '1em' }} onClick={() => speak(props.blocks)}>Speak text</Button>
}

I’ve kept this preview button code to a minimal to make it easier to follow this tutorial. Of course, you could build it out by adding state to show if the preview is processing or make it possible to preview with the different voices that Google’s API supports.

Add the button to SSMLeditor.js:

// SSMLeditor.js
import React, { Fragment } from 'react';
import { BlockEditor } from 'part:@sanity/form-builder';
import PreviewButton from './PreviewButton';

export default function SSMLeditor(props) {
  return (
    <Fragment>
      <BlockEditor {...props} />
      <PreviewButton blocks={props.value} />
    </Fragment>
  );
}

Now you should be able to mark up your text with the different annotations, and hear the result when pushing “Speak text”. Cool, isn’t it?

You’ve Created A Speech Synthesis Editor, And Now What?

If you have followed this tutorial, you have been through how you can use the editor for Portable Text in Sanity Studio to make custom annotations and customize the editor. You can use these skills for all sorts of things, not only to make a speech synthesis editor. You have also been through how to serialize Portable Text into the syntax you need. Obviously, this is also handy if you’re building frontends in React or Vue. You can even use these skills to generate Markdown from Portable Text.

We haven’t covered how you actually use this together with a voice assistant. If you want to try, you can use much of the same logic as with the preview button in a serverless function, and set it as the API endpoint for a fulfillment using webhooks, e.g. with Dialogflow.

If you’d like me to write a tutorial on how to use the speech synthesis editor with a voice assistant, feel free to give me a hint on Twitter or share in the comments section below.

Further Reading on SmashingMag:

Smashing Editorial
(dm, ra, yk, il)

Source: Smashing Magazine, How To Make A Speech Synthesis Editor

How To Build An Endless Runner Game In Virtual Reality (Part 3)

dreamt up by webguru in Uncategorized | Comments Off on How To Build An Endless Runner Game In Virtual Reality (Part 3)

How To Build An Endless Runner Game In Virtual Reality (Part 3)

How To Build An Endless Runner Game In Virtual Reality (Part 3)

Alvin Wan



And so our journey continues. In this final part of my series on how to build an endless runner VR game, I’ll show you how you can synchronize the game state between two devices which will move you one step closer to building a multiplayer game. I’ll specifically introduce MirrorVR which is responsible for handling the mediating server in client-to-client communication.

Note: This game can be played with or without a VR headset. You can view a demo of the final product at ergo-3.glitch.me.

To get started, you will need the following.

  • Internet access (specifically to glitch.com);
  • A Glitch project completed from part 2 of this tutorial. You can start from the part 2 finished product by navigating to https://glitch.com/edit/#!/ergo-2 and clicking “Remix to edit”;
  • A virtual reality headset (optional, recommended). (I use Google Cardboard, which is offered at $15 a piece.)

Step 1: Display Score

The game as-is functions at a bare minimum, where the player is given a challenge: avoid the obstacles. However, outside of object collisions, the game does not provide feedback to the player regarding progress in the game. To remedy this, you will implement the score display in this step. The score will be large text object placed in our virtual reality world, as opposed to an interface glued to the user’s field of view.

In virtual reality generally, the user interface is best integrated into the world rather than stuck to the user’s head.

Score display
Score display (Large preview)

Start by adding the object to index.html. Add a text mixin, which will be reused for other text elements:

<a-assets>
  ...
  <a-mixin id="text" text="
     font:exo2bold;
     anchor:center;
     align:center;"></a-mixin>
  ...
</a-assets>

Next, add a text element to the platform, right before the player:

<!-- Score -->
<a-text id="score" value="" mixin="text" height="40" width="40" position="0 1.2 -3" opacity="0.75"></a-text>

<!-- Player -->
...

This adds a text entity to the virtual reality scene. The text is not currently visible, because its value is set to empty. However, you will now populate the text entity dynamically, using JavaScript. Navigate to assets/ergo.js. After the collisions section, add a score section, and define a number of global variables:

  • score: the current game score.
  • countedTrees: IDs of all trees that are included in the score. (This is because collision tests may trigger multiple times for the same tree.)
  • scoreDisplay: reference to the DOM object, corresponding to a text object in the virtual reality world.
/*********
 * SCORE *
 *********/

var score;
var countedTrees;
var scoreDisplay;

Next, define a setup function to initialize our global variables. In the same vein, define a teardown function.

...
var scoreDisplay;

function setupScore() {
  score = 0;
  countedTrees = new Set();
  scoreDisplay = document.getElementById('score');
}

function teardownScore() {
  scoreDisplay.setAttribute('value', '');
}

In the Game section, update gameOver, startGame, and window.onload to include score setup and teardown.

/********
 * GAME *
 ********/

function gameOver() {
    ...
    teardownScore();
}

function startGame() {
    ...
    setupScore();
    addTreesRandomlyLoop();
}

window.onload = function() {
    setupScore();
    ...
}

Define a function that increments the score for a particular tree. This function will check against countedTrees to ensure that the tree is not double counted.

function addScoreForTree(tree_id) {
  if (countedTrees.has(tree_id)) return;
  score += 1;
  countedTrees.add(tree_id);
}

Additionally, add a utility to update the score display using the global variable.

function updateScoreDisplay() {
  scoreDisplay.setAttribute('value', score);
}

Update the collision testing accordingly in order to invoke this score-incrementing function whenever an obstacle has passed the player. Still in assets/ergo.js, navigate to the collisions section. Add the following check and update.

AFRAME.registerComponent('player', {
  tick: function() {
    document.querySelectorAll('.tree').forEach(function(tree) {
      ...
      if (position.z > POSITION_Z_LINE_END) {
        addScoreForTree(tree_id);
        updateScoreDisplay();
      }
    })
  }
})

Finally, update the score display as soon as the game starts. Navigate to the Game section, and add updateScoreDisplay(); to startGame:

function startGame() {
  ...
  setupScore();
  updateScoreDisplay();
  ...
}

Ensure that assets/ergo.js and index.html match the corresponding source code files. Then, navigate to your preview. You should see the following:

Score display (Large preview)

This concludes the score display. Next, we will add proper start and Game Over menus, so that the player can replay the game as desired.

Step 2: Add Start Menu

Now that the user can keep track of the progress, you will add finishing touches to complete the game experience. In this step, you will add a Start menu and a Game Over menu, letting the user start and restart games.

Let’s begin with the Start menu where the player clicks a “Start” button to begin the game. For the second half of this step, you will add a Game Over menu, with a “Restart” button:

Start and game over menus (Large preview)

Navigate to index.html in your editor. Then, find the Mixins section. Here, append the title mixin, which defines styles for particularly large text. We use the same font as before, align text to the center, and define a size appropriate for the type of text. (Note below that anchor is where a text object is anchored to its position.)

<a-assets>
   ...
   <a-mixin id="title" text="
       font:exo2bold;
       height:40;
       width:40;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

Next, add a second mixin for secondary headings. This text is slightly smaller but is otherwise identical to the title.

<a-assets>
   ...
   <a-mixin id="heading" text="
       font:exo2bold;
       height:10;
       width:10;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

For the third and final mixin, define properties for descriptive text — even smaller than secondary headings.

<a-assets>
   ...
   <a-mixin id="copy" text="
       font:exo2bold;
       height:5;
       width:5;
       opacity:0.75;
       anchor:center;
       align:center;"></a-mixin>
</a-assets>

With all text styles defined, you will now define the in-world text objects. Add a new Menus section beneath the Score section, with an empty container for the Start menu:

<!-- Score -->
...

<!-- Menus -->
<a-entity id="menu-container">
  <a-entity id="start-menu" position="0 1.1 -3">
  </a-entity>
</a-entity>

Inside the start menu container, define the title and a container for all non-title text:

...
  <a-entity id="start-menu" ...>
    <a-entity id="start-copy" position="0 1 0">
    </a-entity>
    <a-text value="ERGO" mixin="title"></a-text>
  </a-entity>
</a-entity>

Inside the container for non-title text, add instructions for playing the game:

<a-entity id="start-copy"...>
  <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text>
</a-entity>

To complete the Start menu, add a button that reads “Start”:

<a-entity id="start-copy"...>
  ...
  <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text>
  <a-box id="start-button" position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box>
</a-entity>

Double-check that your Start menu HTML code matches the following:

<!-- Menus -->
<a-entity id="menu-container">
  <a-entity id="start-menu" position="0 1.1 -3">
    <a-entity id="start-copy" position="0 1 0">
      <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text>
      <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text>
      <a-box id="start-button" position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box>
    </a-entity>
    <a-text value="ERGO" mixin="title"></a-text>
  </a-entity>
</a-entity>

Navigate to your preview, and you will see the following Start menu:


Image of Start menu
Start menu (Large preview)

Still in the Menus section (directly beneath the start menu), add the game-over menu using the same mixins:

<!-- Menus -->
<a-entity id="menu-container">
  ...
  <a-entity id="game-over" position="0 1.1 -3">
    <a-text value="?" mixin="heading" id="game-score" position="0 1.7 0"></a-text>
    <a-text value="Score" mixin="copy" position="0 1.2 0"></a-text>
    <a-entity id="game-over-copy">
      <a-text value="Restart" mixin="heading" position="0 0.7 0"></a-text>
      <a-box id="restart-button" position="0 0.6 -0.05" width="2" height="0.6" depth="0.1"></a-box>
    </a-entity>
    <a-text value="Game Over" mixin="title"></a-text>
  </a-entity>
</a-entity>

Navigate to your JavaScript file, assets/ergo.js. Create a new Menus section before the Game section. Additionally, define three empty functions: setupAllMenus, hideAllMenus, and showGameOverMenu.

/********
 * MENU *
 ********/

function setupAllMenus() {
}

function hideAllMenus() {
}

function showGameOverMenu() {
}

/********
 * GAME *
 ********/

Next, update the Game section in three places. In gameOver, show the Game Over menu:

function gameOver() {
    ...
    showGameOverMenu();
}
```

In `startGame`, hide all menus:

```
function startGame() {
    ...
    hideAllMenus();
}

Next, in window.onload, remove the direct invocation to startGame and instead call setupAllMenus. Update your listener to match the following:

window.onload = function() {
  setupAllMenus();
  setupScore();
  setupTrees();
}

Navigate back to the Menu section. Save references to various DOM objects:

/********
 * MENU *
 ********/

var menuStart;
var menuGameOver;
var menuContainer;
var isGameRunning = false;
var startButton;
var restartButton;

function setupAllMenus() {
  menuStart     = document.getElementById('start-menu');
  menuGameOver  = document.getElementById('game-over');
  menuContainer = document.getElementById('menu-container');
  startButton   = document.getElementById('start-button');
  restartButton = document.getElementById('restart-button');
}

Next, bind both the “Start” and “Restart” buttons to startGame:

function setupAllMenus() {
  ...
  startButton.addEventListener('click', startGame);
  restartButton.addEventListener('click', startGame);
}

Define showStartMenu and invoke it from setupAllMenus:

function setupAllMenus() {
  ...
  showStartMenu();
}

function hideAllMenus() {
}

function showGameOverMenu() {
}

function showStartMenu() {
}

To populate the three empty functions, you will need a few helper functions. Define the following two functions, which accepts a DOM element representing an A-Frame VR entity and shows or hides it. Define both functions above showAllMenus:

...
var restartButton;

function hideEntity(el) {
  el.setAttribute('visible', false);
}

function showEntity(el) {
  el.setAttribute('visible', true);
}

function showAllMenus() {
...

First populate hideAllMenus. You will remove the objects from sight, then remove click listeners for both menus:

function hideAllMenus() {
  hideEntity(menuContainer);
  startButton.classList.remove('clickable');
  restartButton.classList.remove('clickable');
}

Second, populate showGameOverMenu. Here, restore the container for both menus, as well as the Game Over menu and the ‘Restart’ button’s click listener. However, remove the ‘Start’ button’s click listener, and hide the ‘Start’ menu.

function showGameOverMenu() {
  showEntity(menuContainer);
  hideEntity(menuStart);
  showEntity(menuGameOver);
  startButton.classList.remove('clickable');
  restartButton.classList.add('clickable');
}

Third, populate showStartMenu. Here, reverse all changes that the showGameOverMenu effected.

function showStartMenu() {
  showEntity(menuContainer);
  hideEntity(menuGameOver);
  showEntity(menuStart);
  startButton.classList.add('clickable');
  restartButton.classList.remove('clickable');
}

Double-check that your code matches the corresponding source files. Then, navigate to your preview, and you will observe the following behavior:

Start and Game Over menus (Large preview)

This concludes the Start and Game Over menus.

Congratulations! You now have a fully functioning game with a proper start and proper end. However, we have one more step left in this tutorial: We need to synchronize the game state between different player devices. This will move us one step closer towards multiplayer games.

Step 3: Synchronizing Game State With MirrorVR

In a previous tutorial, you learned how to send real-time information across sockets, to facilitate one-way communication between a server and a client. In this step, you will build on top of a fully-fledged product of that tutorial, MirrorVR, which handles the mediating server in client-to-client communication.

Note: You can learn more about MirrorVR here.

Navigate to index.html. Here, we will load MirrorVR and add a component to the camera, indicating that it should mirror a mobile device’s view where applicable. Import the socket.io dependency and MirrorVR 0.2.3.

Next, add a component, camera-listener, to the camera:

<a-camera camera-listener ...>

Navigate to assets/ergo.js. In this step, the mobile device will send commands, and the desktop device will only mirror the mobile device.

To facilitate this, you need a utility to distinguish between desktop and mobile devices. At the end of your file, add a mobileCheck function after shuffle:

/**
 * Checks for mobile and tablet platforms.
 */
function mobileCheck() {
  var check = false;
  (function(a){if(/(android|bbd+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};

First, we will synchronize the game start. In startGame, of the Game section, add a mirrorVR notification at the end.

function startGame() {
  ...
  if (mobileCheck()) {
    mirrorVR.notify('startGame', {})
  }
}

The mobile client now sends notifications about a game starting. You will now implement the desktop’s response.

In the window load listener, invoke a setupMirrorVR function:

window.onload = function() {
  ...
  setupMirrorVR();
}

Define a new section above the Game section for the MirrorVR setup:

/************
 * MirrorVR *
 ************/

function setupMirrorVR() {
  mirrorVR.init();
}

Next, add keyword arguments to the initialization function for mirrorVR. Specifically, we will define the handler for game start notifications. We will additionally specify a room ID; this ensures that anyone loading your application is immediately synchronized.

function setupMirrorVR() {
  mirrorVR.init({
    roomId: 'ergo',
    state: {
      startGame: {
        onNotify: function(data) {
          hideAllMenus();
          setupScore();
          updateScoreDisplay();
        }
      },
    }
  });
}

Repeat the same synchronization process for Game Over. In gameOver in the Game section, add a check for mobile devices and send a notification accordingly:

function gameOver() {
  ...
  if (mobileCheck()) {
    mirrorVR.notify('gameOver', {});
  }
}

Navigate to the MirrorVR section and update the keyword arguments with a gameOver listener:

function setupMirrorVR() {
  mirrorVR.init({
    state: {
      startGame: {...
      },
      gameOver: {
        onNotify: function(data) {
          gameOver();
        }
      },
    }
  }) 
}

Next, repeat the same synchronization process for the addition of trees. Navigate to addTreesRandomly in the Trees section. Keep track of which lanes receive new trees. Then, directly before the return directive, and send a notification accordingly:

function addTreesRandomly(...) {
  ...
  var numberOfTreesAdded ...
  var position_indices = [];
  trees.forEach(function (tree) {
    if (...) {
      ...
      position_indices.push(tree.position_index);
    }
  });

  if (mobileCheck()) {
    mirrorVR.notify('addTrees', position_indices);
  }
  return ...
}

Navigate to the MirrorVR section, and update the keyword arguments to mirrorVR.init with a new listener for trees:

function setupMirrorVR() {
  mirrorVR.init({
    state: {
      ...
      gameOver: {...
      },
      addTrees: {
        onNotify: function(position_indices) {
          position_indices.forEach(addTreeTo)
        }
      },
    }
  }) 
}

Finally, we synchronize the game score. In updateScoreDisplay from the Score section, send a notification when applicable:

function updateScoreDisplay() {
  ...
  if (mobileCheck()) {
    mirrorVR.notify('score', score);
  }
}

Update the mirrorVR initialization for the last time, with a listener for score changes:

function setupMirrorVR() {
  mirrorVR.init({
    state: {
      addTrees: {
      },
      score: {
        onNotify: function(data) {
          score = data;
          updateScoreDisplay();
        }
      }
    }
  });
}

Double-check that your code matches the appropriate source code files for this step. Then, navigate to your desktop preview. Additionally, open up the same URL on your mobile device. As soon as your mobile device loads the webpage, your desktop should immediately start mirroring the mobile device’s game.

Here is a demo. Notice that the desktop cursor is not moving, indicating the mobile device is controlling the desktop preview.

Final result of the endless runner game with MirrorVR game state synchronization (Large preview)

This concludes your augmented project with mirrorVR.

This third step introduced a few basic game state synchronization steps; to make this more robust, you could add more sanity checks and more points of synchronization.

Conclusion

In this tutorial, you added finishing touches to your endless runner game and implemented real-time synchronization of a desktop client with a mobile client, effectively mirroring the mobile device’s screen on your desktop. This concludes the series on building an endless runner game in virtual reality. Along with A-Frame VR techniques, you’ve picked up 3D modeling, client-to-client communication, and other widely applicable concepts.

Next steps can include:

  • More Advanced Modeling
    This means more realistic 3D models, potentially created in a third-party software and imported. For example, (MagicaVoxel) makes creating voxel art simple, and (Blender) is a complete 3D modeling solution.
  • More Complexity
    More complex games, such as a real-time strategy game, could leverage a third-party engine for increased efficiency. This may mean sidestepping A-Frame and webVR entirely, instead publishing a compiled (Unity3d) game.

Other avenues include multiplayer support and richer graphics. With the conclusion of this tutorial series, you now have a framework to explore further.

Smashing Editorial
(rb, ra, il)

Source: Smashing Magazine, How To Build An Endless Runner Game In Virtual Reality (Part 3)