Playable Places: Google’s Answer to Pokemon Go

One of the most intriguing things I’ve learned about at Google I/O is the Google Maps Platform gaming offerings and, among those, playable places.

The base concept is this:

Take Ingress and Pokemon Go and make them playable worldwide without having to collect data about the structures where you want to place markers for interaction to happen. With that base take out the possibility of playing in inappropriate places (like cemeteries) and other private properties where you have not been allowed to play, Google has done the work for us in identifying places where we can run our apps without fear of upsetting property owners or playing in sensitive locations.

You also have the ability to use geofences to make certain activities only happen where we want them to be. For example, we want an event for a scavenger hunt only to happen in a single place in the playing area; we can create a geofence around that place so that the event will only trigger when inside the region defined by the fence. It gives developers a lot more flexibility in creating and configuring our users’ experience.

These are some games using the maps platform that have already been announced in the Map Platform website and will release in the near future (click on the video title for a larger version).



Ideas and Examples

Beyond the games presented above, here are some ideas of what we can use playable places for. Some of these ideas come from having worked at Geoteaming creating team building scavenger hunts. Some come from playing board, computer and role-playing games.

Scavenger Hunts and Team Building Exercises

Other than games, scavenger hunts are the essential activity to do in a virtual world. We can do generic scavenger hunts where the objective can be to collect points or trophies by visiting and/or completing challenges in as many locations within the playing area as possible.

These missions could be as simple as capturing an enemy monster (think Pokemon Go), or working to decipher a puzzle at the location, or any other type of individual or team-oriented activity you can think about.

Marketing for a geographical location

A variation on the scavenger hunt theme is to create marketing experiences.

For example, A business association can create a marketing campaign for their members. Get specific rewards (physical and virtual) when players visit participating locations.

The organizer can then choose to award physical prizes to the person or group who gets the highest score or who have shared the adventure in Social Media.

Laser Tag

Like Ingress live events or Pokemon Go Raid Battles large-scale events can be the main activity or the capstone event for a smaller or shorter duration game.

Conclusion and Next Steps

These are just first ideas, I’ll be playing with more ideas and following the process to see how expensive it would be to get some of them implemented.

Other questions are more in the line of development environment required to use the APIs.

  • Am I allowed to create web applications to use the API?
  • Am I limited to Unity and mobile devices?
  • If I follow the scavenger hunt model, do I need to create one application per geographical region?

The next steps are to write down a concrete proposal and then contact Google to see what would next steps be and where it’ll go from there.

Link rel=”canonical” what it is and why you want to use it

Since I first saw it in AMP, I thought that link rel="canonical" was part of the AMP library. I was surprised to see that it’s more than just an AMP thing. In this post, we’ll discuss what are canonical links and documents, why we need to use it, and some gotchas to consider before going all in with canonical links.

What it is and Why should you use them?

There are times when I want to publish the same content in more than one site, like my personal blog and my employer’s blog; or I may want to publish content in Medium without losing the SEO for my own site.

This is where canonical links come in. When two or more pages point to the same content, we can tell the search engine crawler which ones are the primary and that both pages refer to the same content. As far as the Googlebot is concerned, the pages are merged together.

Say, for example, that my primary resource is: https://blog.mysite.net/ we can do the following:

In all sites where we’ve added our content, we add the following link element to the pages:

<link rel="canonical" href="https://blog.mysite.net/">

Example: Medium and WordPress

Another example of when this would be useful is when we upload content from another site to the Medium platform. During the import process, you can tell Medium where the canonical content is located. This will help people find your content and make it easier to share your content in multiple places.

In your main pages use the standard canonical link:

<link rel="canonical" href="https://blog.mysite.net/">

In Medium, use the import post tool to import your posts into the platform. The tool will add a canonical link based on the URL you are importing.

Gotchas

Canonical links come with their own baggage. The main two issues I find annoying about canonical links is that they may still not create unique references to the original resources. The two main things that concern me are below.

Be careful with parameters in a URL

Query string parameters like those we use in Analytics or advertising may create duplicate entries on the Googlebot search crawl and point to two different pages that are actually the same page with different URL parameters. This may or may not cause problems down the line but it pays to think about it now rather than when you have to put a fire out because of it.

For more information check Using Rel Canonical on All Pages for Duplicate Content Protection from thesempost.com.

Bing doesn’t use Canonical Links!

The other issue is that not all search engines use canonical links. Engines like Bing use a different way to remove different parts of the URL to tell the crawler which of a set of URLs to crawl and which not to.

Bing explains why this is important:

When you have duplicate problems due to extra URLs parameters, using the URL Normalization feature in the Bing Webmaster Tools is the preferred method as you are telling us which parameters can go away; we call this URL normalization. By normalizing, we mean that our crawler will not visit the URLs with extra parameters except for an occasional test of the quality of the normalization rules.

From: Better than canonical; URL Normalization

Please check search engines other than Google if they support canonical links and, if they don’t, how to make sure that similar URLs will not be crawled.

Links and Resources

Quick Note: Using Viewport Units

These little darling units allow for some really nice effects and typographical work. We’ll explore what they are and a couple effects we can use them on.

vh and vw

The two basic units for viewport measurement are vh and vw which track like so:

  • vw: 1/100th (1%) viewport width
  • vh: 1/100th (1%) viewport height

The units will remain constant. 1/100th of the viewport width will remain 1/100th regardless of how wide or narrow the viewport becomes. to make an element span half the screen horizontally we could do something like this:

hero {
  width: 50vw;
}

There may also be elements like a masthead that we want to place in the top 20% of the screen we could combined with position: fixed to do something like this:

.masthead {
  height: 20vh;
  position: fixed;
}

vmin and vmax

These two units are a little more complicated, they will take the value from whatever side matches the minimum or maximum value.

  • vmin: 1/100th (1%) of the smallest side
  • vmax: 1/100th (1%) of the largest side

I’ve always had a hard time when it comes to choosing how/when to use vmin and vmax. The default value for these will also depend on the device’s orientation and how much has the user resized the browser window:

vmin uses the ratio of the smallest side. If the height of the browser window is less than its width, 1vmin will be equivalent to 1vh. Otherwise 1vmin is 1vw.

vmax is the opposite: 1vmax equals 1vw if the viewport is wider than it is tall; If not 1vmax is equal to 1vh.

This means we can use these values inside device orientation media queries ((@media screen and (orientation: portrait) or @media screen and (orientation : landscape)) since these units are already designed to take the value from either the height or the width.

Some examples

Here are some examples of what you can do with these units.

Truly responsive content

A couple years ago I came across a Pen from Brian Haferkamp called 2-Up Magazine Layout and I was amazed at the level of complexity he crammed into the Pen and the attention to detail that he paid too little details such as responsive sizing of the content and how the 2-column layout would automatically resize itself in narrower viewports.

I took the idea, having a fixed image next to scrolling text. I think I’ve finally figured out what the technique is and how to implement it. This is a stripped down version of my pen.

The first thing we do is set the wrapper container to be a flex container using display: flex. We don’t need to set the children items as flex-item. This is automatically done when we set the parent element’s display.

background-size: cover tells the browser to cover the full dimension of the container, in this case, 100vh and 50vw.

QWe can play with the image positioning using background-position to place different parts of the image in the viewport we’re covering.

The flex property is the shorthand for flex-grow, flex-shrink and flex-basis combined. With these values we’re telling .poster-image elements to take 50% of the parent container size before other elements are distributed in it.

For the .text element(s) we give them padding and a left margin. We also make sure that they are at the top of the stacking order.

.article-wrapper {
  display: flex;
}

.poster-image {
  position: fixed;
  background-image: url("path/to/image");
  background-size: cover;
  // background-position: center;
  height: 100vh;
  width: 50vw;
  flex: 0 0 50%;
  z-index: 1;
}

.text {
  padding: 100px 60px 60px;
  margin-left: 50%;
  z-index: 10;
}

The HTML is a series of div containers that hold:

  • The article wrapper element (.article-wrapper)
  • The poster image (.poster-image)
  • The text of the article (.text)
  • And the article content itself (article)
<div class="article-wrapper">
  <div class="poster-image"></div>
  <div class="text">
    <h1>Article Title</h1>
    <article>
      <p></p>
    </article>
  </div>
</div>

Some considerations

There are a couple things I haven’t quite figure out to make this work reliably for all browsers.

Scrollbars

Browsers have inconsistent behaviors regarding scrollbars across operating systems. A very basic way to address the issue is to script the behavior of scrollbars: If the height of an element’s content, including content not visible on the screen due to overflow (in the read-only property element.scrollHeight of the document element) is taller than the inner height of the document element in pixels, including padding but not the horizontal scrollbar height, border, or margin (in the read-only clientHeightproperty of the document element) the we force scrollbars, otherwise we hide them.

const element = document.documentElement;

if(element.scrollHeight > element.clientHeight) {
  // Overflow detected; force scroll bar
  element.style.overflow = 'scrollbar';
} else {
  // No overflow detected; prevent scroll bar
  element.style.overflow = 'hidden';
}

Yes, this is a simplistic solution. If you want more details and a much more thorough solution to the problem, check Crossbrowser JavaScript Scrollbar Detection by Tyler Cipriani

Legibility on smaller screens

If we set the font to a viewport unit we need to be very careful that the text is readable on smaller screens. The best and most reliable way to do this is to create media queries for the smaller form factors and set the font to an absolute value or em or rem to make sure the text is still readable.

Links and Resources

Inspecting your variable fonts

One of the most frustrating things of working with Variable Fonts is that we have no real way (short of bugging the font creator to provide them for you) to detect what values work for each scale, how they scale and what, if any, pre-defined values are available for each font.

Axis Praxis is a good starting point. It provides you with a way to test and play with Variable Fonts but you get only one experiment at a time.

Axis Praxis App on Firefox Nightly 61.0a
Axis Praxis App on Firefox Nightly 61.0a

Wakamai Fondue (funny way of saying what can my font do?) provides a more thorough and more fun way to test specific variable fonts that you want to use in your projects.

Wakamai Fondue on Firefox Nightly 61.0a
Wakamai Fondue on Firefox Nightly 61.0a

But it does more than just show you what you can do with the font. It creates visual displays of what the selected instance (pre-configured axes combination), gives you the generated CSS and what values the instance used for the axes on the font.

Wakamai fondue VF font instance example
Wakamai fondue VF font instance example
Wakamaifondue VF font instance CSS listing
Wakamaifondue VF font instance CSS listing

It also shows you all the classes it creates for the instances available on the font. If you’ve loaded the font using @font-face you can just copy the example into your document to test the font.

Wakamaifondue VF font character listing
Wakamaifondue VF font character listing

Wakamaifondue will also list the characters available on the font and give you a way to change the variable axes to see what individual characters will look like under different axes combinations.

Wakamaifondue display Opentype layout features available to the font. The ones enabled will be added to the generated CSS style sheet
Wakamaifondue display Opentype layout features available to the font. The ones enabled will be added to the generated CSS style sheet

If the font has Opentype layout features available they will appear here along with a way to show it and add it to the CSS stylesheet we’ll discuss in the next section.

There may not be any features under this section. It depends on the font.

Wakamaifondue Generated CSS
Wakamaifondue Generated CSS

The most important part, to me, is that Wakamaifondue generates a CSS style sheet with all the variable fonts instances and the open type features available to the font.

The CSS uses variables to set the different layout features rather than font-variant-*. font-variation-settings has wider support but it doesn’t cascade like font-variant-*; See CSS WG Issue 552 for more details on how .

Using variables mean that the features we add will work consistently until more browsers implement font variations and they work consistently across browsers.

You can also combine features to get the results you need.

The example below shows the style sheet generated for the Roboto Regular variable font. This will give you an idea of what you can expect for a fairly complete font.

/**  * CSS for                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 Regular
 * Generated by Wakamai Fondue - https://wakamaifondue.com
 * by Roel Nieskens/PixelAmbacht - https://pixelambacht.nl
 */

/* Set custom properties for each layout feature */
:root {
    --roboto-regular-c2sc: "c2sc" off;
    --roboto-regular-dlig: "dlig" off;
    --roboto-regular-dnom: "dnom" off;
    --roboto-regular-frac: "frac" off;
    --roboto-regular-lnum: "lnum" off;
    --roboto-regular-numr: "numr" off;
    --roboto-regular-onum: "onum" off;
    --roboto-regular-pnum: "pnum" off;
    --roboto-regular-salt: "salt" off;
    --roboto-regular-smcp: "smcp" off;
    --roboto-regular-ss01: "ss01" off;
    --roboto-regular-ss02: "ss02" off;
    --roboto-regular-ss03: "ss03" off;
    --roboto-regular-ss04: "ss04" off;
    --roboto-regular-ss05: "ss05" off;
    --roboto-regular-ss06: "ss06" off;
    --roboto-regular-ss07: "ss07" off;
    --roboto-regular-tnum: "tnum" off;
    --roboto-regular-unic: "unic" off;
    --roboto-regular-cpsp: "cpsp" off;
}

/* If class is applied, update custom property and
   apply modern font-variant-* when supported */
.roboto-regular-c2sc {
    --roboto-regular-c2sc: "c2sc" on;
}

.roboto-regular-dlig {
    --roboto-regular-dlig: "dlig" on;
}

@supports (font-variant-ligatures: discretionary-ligatures) {
    .roboto-regular-dlig {
        --roboto-regular-dlig: "____";
        font-variant-ligatures: discretionary-ligatures;
    }
}

.roboto-regular-dnom {
    --roboto-regular-dnom: "dnom" on;
}

.roboto-regular-frac {
    --roboto-regular-frac: "frac" on;
}

@supports (font-variant-numeric: diagonal-fractions) {
    .roboto-regular-frac {
        --roboto-regular-frac: "____";
        font-variant-numeric: diagonal-fractions;
    }
}

.roboto-regular-lnum {
    --roboto-regular-lnum: "lnum" on;
}

@supports (font-variant-numeric: lining-nums) {
    .roboto-regular-lnum {
        --roboto-regular-lnum: "____";
        font-variant-numeric: lining-nums;
    }
}

.roboto-regular-numr {
    --roboto-regular-numr: "numr" on;
}

.roboto-regular-onum {
    --roboto-regular-onum: "onum" on;
}

@supports (font-variant-numeric: oldstyle-nums) {
    .roboto-regular-onum {
        --roboto-regular-onum: "____";
        font-variant-numeric: oldstyle-nums;
    }
}

.roboto-regular-pnum {
    --roboto-regular-pnum: "pnum" on;
}

@supports (font-variant-numeric: proportional-nums) {
    .roboto-regular-pnum {
        --roboto-regular-pnum: "____";
        font-variant-numeric: proportional-nums;
    }
}

.roboto-regular-salt {
    --roboto-regular-salt: "salt" on;
}

.roboto-regular-smcp {
    --roboto-regular-smcp: "smcp" on;
}

@supports (font-variant-caps: small-caps) {
    .roboto-regular-smcp {
        --roboto-regular-smcp: "____";
        font-variant-caps: small-caps;
    }
}

.roboto-regular-ss01 {
    --roboto-regular-ss01: "ss01" on;
}

.roboto-regular-ss02 {
    --roboto-regular-ss02: "ss02" on;
}

.roboto-regular-ss03 {
    --roboto-regular-ss03: "ss03" on;
}

.roboto-regular-ss04 {
    --roboto-regular-ss04: "ss04" on;
}

.roboto-regular-ss05 {
    --roboto-regular-ss05: "ss05" on;
}

.roboto-regular-ss06 {
    --roboto-regular-ss06: "ss06" on;
}

.roboto-regular-ss07 {
    --roboto-regular-ss07: "ss07" on;
}

.roboto-regular-tnum {
    --roboto-regular-tnum: "tnum" on;
}

@supports (font-variant-numeric: tabular-nums) {
    .roboto-regular-tnum {
        --roboto-regular-tnum: "____";
        font-variant-numeric: tabular-nums;
    }
}

.roboto-regular-unic {
    --roboto-regular-unic: "unic" on;
}

@supports (font-variant-caps: unicase) {
    .roboto-regular-unic {
        --roboto-regular-unic: "____";
        font-variant-caps: unicase;
    }
}

.roboto-regular-cpsp {
    --roboto-regular-cpsp: "cpsp" on;
}

/* Apply current state of all custom properties
   whenever a class is being applied */
.roboto-regular-c2sc,
.roboto-regular-dlig,
.roboto-regular-dnom,
.roboto-regular-frac,
.roboto-regular-lnum,
.roboto-regular-numr,
.roboto-regular-onum,
.roboto-regular-pnum,
.roboto-regular-salt,
.roboto-regular-smcp,
.roboto-regular-ss01,
.roboto-regular-ss02,
.roboto-regular-ss03,
.roboto-regular-ss04,
.roboto-regular-ss05,
.roboto-regular-ss06,
.roboto-regular-ss07,
.roboto-regular-tnum,
.roboto-regular-unic,
.roboto-regular-cpsp {
    font-feature-settings: var(--roboto-regular-c2sc), var(--roboto-regular-dlig), var(--roboto-regular-dnom), var(--roboto-regular-frac), var(--roboto-regular-lnum), var(--roboto-regular-numr), var(--roboto-regular-onum), var(--roboto-regular-pnum), var(--roboto-regular-salt), var(--roboto-regular-smcp), var(--roboto-regular-ss01), var(--roboto-regular-ss02), var(--roboto-regular-ss03), var(--roboto-regular-ss04), var(--roboto-regular-ss05), var(--roboto-regular-ss06), var(--roboto-regular-ss07), var(--roboto-regular-tnum), var(--roboto-regular-unic), var(--roboto-regular-cpsp);
}

/* Variable instances */
.roboto-regular-thin {
    font-variation-settings: "wdth" 100, "wght" 0;
}

.roboto-regular-light {
    font-variation-settings: "wdth" 100, "wght" 45;
}

.roboto-regular-regular-i {
    font-variation-settings: "wdth" 100, "wght" 90;
}

.roboto-regular-medium {
    font-variation-settings: "wdth" 100, "wght" 135;
}

.roboto-regular-bold {
    font-variation-settings: "wdth" 100, "wght" 173;
}

.roboto-regular-black {
    font-variation-settings: "wdth" 100, "wght" 212.5;
}

.roboto-regular-condensed-light {
    font-variation-settings: "wdth" 84, "wght" 45;
}

.roboto-regular-condensed-regular {
    font-variation-settings: "wdth" 84, "wght" 90;
}

.roboto-regular-condensed-bold {
    font-variation-settings: "wdth" 84, "wght" 175;
}

CSS Shapes: Once more, with feeling

CSS Shapes are an awesome way to make our designs look less like boxes inside boxes inside boxes. It let us wrap text around an image regardless of its shape.

I’ve written before about shapes. The posts, in reverse chronological order:

There are news and new tools to use with shapes and that got me excited again so here we go again. 🙂

So What Are CSS Shapes?

CSS Shapes describe geometric shapes for use in CSS. For Level 1, CSS Shapes can be applied only to floats. A circle shape on a float will cause inline content to wrap around the circle shape instead of the float’s bounding box.

We can play with standard shapes like circles or ellipses or we can create, irregular polygons or other custom shapes. The text will automatically flow around them.

So you may be asking why is this important… who actually cares?

For a long time, I’ve thought that it is possible to create web layouts that are as engaging as those in printed media. Shapes are a step in the right direction and it opens possibilities for more creative views of the same content. For those browsers that don’t support shapes, the text will flow around the box like we are used to… not that big a loss.

Shapes: from the simple to the complex

One of the first examples I saw was text shaped outside a circular image.

See the Pen Shape Outside Example by Carlos Araya (@caraya) on CodePen.

But there are much more intricate shapes you can draw and use your content. We can start with triangles and other geometrical shapes.

See the Pen Combing CSS Shapes & clip-path by Carlos Araya (@caraya) on CodePen.

When creating the additional shapes you can leverage polygons to make the content more interesting, even if the shape itself may not be.

See the Pen CSS Shapes Demo #11 by Carlos Araya (@caraya) on CodePen.

You can also give the impression of the text being inside an image (what the level shape specification calls shape inside) by placing the text between two images and using shape-outside on each one of them.

The best example of this techniques is Adobe’s Demo for Alice in Wonderland by Lewis Carroll and document in the Adobe Web Platform blog Retrieved from the Wayback machine.

Why New Tools?

The biggest problems when creating polygons to use with CSS Shapes is that they are far from intuitive. To create a triangle you have to use the following CSS:

img#penetrator {
  width: 50%;
  margin-left: -12%;
  float: left;
  shape-outside: polygon(24% 0, 24% 100%, 100% 54%);
}

The Polygon gets more complicated the larger and the more irregular shape you want to use. Coding the polygon shape by hand but it’s complicated enough to make it very hard.

That’s where Firefox’s CSS Shape Editor comes in.

If you’ve already created the shape you can customize it, however, it will not let you create a brand new shape visually or add anchor points to an existing shape.

It’s not a complete shape editor but it’s still the best way to play with shapes in a live browser environment.