Tag Archives: building

Thumbnail

Landing Page Essentials: A Free Video Crash Course from Unbounce and Skillshare

Ever heard the saying “Cart before the horse”? Or “You have to crawl before you can walk”? Or “You can’t put lipstick on a landing page with 27 links”?

That last one may be exclusive to landing page software employees, but the sentiment is the same. Unless the foundation of your landing page is strong, any optimization beyond that will be a waste of your time—and ad spend. Because even the slickest, fanciest landing page will leak precious conversions if it lacks certain crucial elements.

For the sake of those ad dollars, let’s go back to basics.

In collaboration with our friends (and customers!) at Skillshare, we’ve created a free video crash course on the fundamentals of a high-converting landing page. Whether you’re building your first page or just want a refresher, you’ll get a checklist to set up each of your pages for success.

The full course, Creating Dedicated Landing Pages: How to Get Better ROI for Your Marketing Spend, is hosted by Unbounce VP of Product Marketing Ryan Engley and comprised of 11 videos totalling a quick 31 minutes. Sign up for a free Skillshare account and dive right into binge mode, or keep scrolling for an overview of what every landing page you create should have.

Bonus: Skillshare is offering 2 free months and access to thousands of other marketing classes just for signing up through our course.

Who’s it for?

Anyone running marketing campaigns! But in particular, those who execute on them.

Whether you’re responsible for launching paid advertising campaigns, build and design landing pages yourself, or work with designers and copywriters to create them, this course will ensure you’ve covered every base to create a compelling and high-converting post-click experience.

In a nutshell: It’s for anyone who runs paid marketing campaigns and wants to get the most bang for their buck.

What will it teach me?

In 11 videos, Ryan will take you through the process of creating a persuasive marketing campaign, cover each step of building a successful landing page within it, and explain the “why” behind it all so you’re taught to fish instead of just being handed the fish.

A few tidbits to start

Attention Ratio

If you’re thinking, “What’s wrong with sending people to my homepage?” then Attention Ratio is a great place to start.

“Your website is a bit of a jack of all trades,” Ryan explains. “Usually it’ll have a ton of content for SEO purposes, maybe information about your team…but if you’re running a marketing campaign and you have a single call to action in mind, your website’s not going to do you any favours.”

The more links you have on your page, the more distractions there are from your campaign’s CTA. You don’t want people to explore—you want them to act. And an Attention Ratio of 1:1 is a powerful way of achieving that.

Learn more about Attention Ratio in chapter three.

Unique Selling Proposition (USP)

Somewhat self-explanatory, your Unique Selling Proposition describes the benefit you offer, how you solve for prospects’ needs, and what distinguishes you from the competition. This doesn’t all have to fit in one sentence, rather, it can reveal itself throughout the page. But if you’re going to focus on one place to do the “heavy lifting,” as Ryan calls it, this place should be your headline and subhead.

Take Skillshare’s landing page for a content marketing course by Buzzfeed’s Matt Bellassai (if his name doesn’t ring a bell, Google him, grab some popcorn, and come back to us with a few laughter-induced tears streaming down your face). Without even looking at the rest of the page, you know exactly what you’ll get out of this course and how it will help you achieve a goal.

Learn more about Unique Selling Proposition in chapter five.

Social Proof

What’s more convincing than word of mouth? Since we don’t advise stalking and hiring people’s friends to tell prospects how great you are, the next best thing is to feature testimonials on your landing page. The key here is that you’re establishing trust and credibility by having someone else back you up.

Customer quotes, case studies, and product reviews are just a few of the many ways you can inject social proof into your landing page. Think of it as a “seal of approval” woven into your story that shows prospects you deliver on the promise of your Unique Selling Proposition.

Customer testimonials serve as the proof in your pudding.

Learn more about Social Proof in chapter eight.

And now for all the bits

Watch all 11 episodes of Creating Dedicated Landing Pages: How to Get Better ROI for Your Marketing Spend to set your landing pages up for success in less time than it takes to finish your lunch break. Beyond being 100% free, it’ll save you a lot of guesswork in building landing pages that convert and precious ad spend to boot. So settle in for a mini binge watch with a sandwich on the company tab—you earned it.

View article:  

Landing Page Essentials: A Free Video Crash Course from Unbounce and Skillshare

Thumbnail

Building A PWA Using Angular 6




Building A PWA Using Angular 6

Ahmed Bouchefra



In this tutorial, we’ll be using the latest Angular 6 to build a PWA by implementing the core tenets that make a PWA. We’ll start by creating a front-end web application that consumes a JSON API. For this matter, we’ll be using the Angular HttpClient module to send HTTP requests to a statically JSON API generated from the Simplified JavaScript Jargon GitHub repository. We’ll also use Material Design for building the UI via the Angular Material package.

Next, we’ll use the “Audits” panel (Lighthouse) from Chrome DevTools to analyze our web application against the core tenets of PWAs. Finally, we’ll explain and add the PWA features to our web application according to the “Progressive Web App” section in the Lighthouse report.

Before we start implementing our PWA, let’s first introduce PWAs and Lighthouse.

Recommended reading: Native And PWA: Choices, Not Challengers!

What’s A PWA?

A Progressive Web App or PWA is a web application that has a set of capabilities (similar to native apps) which provide an app-like experience to users. PWAs need to meet a set of essential requirements that we’ll see next. PWAs are similar to native apps but are deployed and accessible from web servers via URLs, so we don’t need to go through app stores.

A PWA needs to be:

  • Progressive
    Work for every user, regardless of browser choice, because they are built with progressive enhancement as a core tenet.
  • Responsive
    Fit any form factor, desktop, mobile, tablet, or whatever is next.
  • Connectivity independent
    Enhanced with service workers to work offline or on low-quality networks.
  • App-like
    Use the app-shell model to provide app-style navigation and interactions.
  • Fresh
    Always up-to-date thanks to the service worker update process.
  • Safe
    Served via HTTPS to prevent snooping and ensure content has not been tampered with.
  • Discoverable
    Are identifiable as “applications” thanks to W3C manifests and service worker registration scope allowing search engines to find them.
  • Re-engageable
    Make re-engagement easy through features like push notifications.
  • Installable
    Allow users to “keep” apps they find most useful on their home screen without the hassle of an app store.
  • Linkable
    Easily share via URL and not require complex installation.

Introducing Lighthouse

Lighthouse is an open-source auditing tool created by Google which can be used to audit websites and applications for accessibility performance, SEO, best practices and PWA features.

You can access Lighthouse from the Audit tab in Chrome DevTools as a module in Node.js or as a CLI tool. You can use Lighthouse by providing an URL and then running the audits which will provide you with a report containing the auditing results which are basically suggestions on how you can improve your web application.

Installing Angular CLI v6 And Generating A Project

In this section, we’ll install the latest version of Angular CLI then we’ll use it to create a new Angular 6 project.

Angular CLI requires Node.js >= 8.9+ so first make sure you have the required version installed by running the following command:

$ node -v

Node.js version


Checking Node version. (Large preview)

In case you don’t have Node.js installed, you can simply head on to the official Node download page and grab the Node binaries for your system.

Now, you can go ahead and install the latest version of Angular CLI by running:

$ npm install -g @angular/cli 

Note: Depending on your npm configuration, you may need to add _sudo_ to install packages globally.

You can generate your Angular 6 project by running the following command in your terminal:

$ ng new pwademo

This will create a project with a structure that looks like:


Angular project structure


Angular project structure. (Large preview)

Most work that’s done will be inside the src/ folder that contains the source code of the application.

Creating The Angular Application

After generating a project, we’ll build a web application that consumes a JSON API and displays the items on the home page. We’ll use the HttpClient service for sending HTTP requests and Angular Material for building the UI.

Adding Angular Material

Thanks to Angular CLI v6 and the new ng add command, adding Angular Material to your project is only one command away. You just need to run the following command from your terminal:

$ cd pwademo
$ ng add @angular/material

Adding Angular Material


Adding Angular Material. (Large preview)

You can see from the screenshot that the command installs the required package from npm and update a bunch of files for setting up Angular Material in your project which previously needed manual updates.

Setting Up HttpClient And Consuming The JSON API

Now, let’s setup the Angular project to use HttpClient for sending HTTP requests. First, you need to import the HttpClientModule module in the main application module in the src/app/app.module.ts file:

/*...*/
import  HttpClientModule  from  '@angular/common/http';
@NgModule(
declarations: [
AppComponent
],
imports: [
/*...*/
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
)
export  class  AppModule  

That’s it. We can now inject and use HttpClient in any component or service that belongs to the main module.

For demo purposes, we’ll consume a statically generated JSON API from the Simplified JavaScript Jargon GitHub repository. If you are consuming any other resource, make sure you have CORS enabled so the browser doesn’t disallow reading the remote resource due to the Same Origin Policy.

Let’s create a service that interfaces with the API. Inside your project folder, run:

$ ng g service api

This will create a service called ApiService in the src/app/api.service.ts file.

Now open the src/app/api.service.ts file and update it to reflect the following changes:

import  Injectable  from  '@angular/core';
import  HttpClient  from  '@angular/common/http';
import  Observable  from  'rxjs';

export  interface  Item
name:  string;
description:  string;
url:  string;
html:  string;
markdown:  string;


@Injectable(
providedIn:  'root'
)

export  class  ApiService 
private  dataURL:  string  =  "https://www.techiediaries.com/api/data.json";
constructor(private  httpClient:  HttpClient) 
fetch():  Observable<Item[]>
return <Observable<Item[]>this.httpClient.get(this.dataURL);

}

We first imported the HttpClient and Observable classes then injected the HttpClient in the constructor as httpClient and added a fetch() method which calls the get() method of HttpClient (for sending an HTTP GET request to our JSON endpoint) and returns an Observable that we can subscribe to later.

We also declared an Item interface which represents a single item of the returned JSON data.

Next import this service from the application component. Open the src/app/app.component.ts file and add:

import  Component, OnInit  from  '@angular/core';
import  ApiService  from  './api.service';
import  Item  from  './api.service';

@Component(
selector:  'app-root',
templateUrl:  './app.component.html',
styleUrls: ['./app.component.css']
)
export  class  AppComponent  implements  OnInit
title  =  'pwademo';
items:  Array<Item>;
constructor(private  apiService:  ApiService)

ngOnInit()
this.fetchData();

fetchData()
this.apiService.fetch().subscribe((data:  Array<Item>)=>
console.log(data);
this.items  =  data;
, (err)=>
console.log(err);
);
}
}

We import the ApiService that we created before and we inject it as apiService, we also import the Item class which represents a single item of our JSON data and we declare the items variable of type Array<Item> which will hold the fetched items.

Next, we add a fetchData() method which calls our fetch() method that we defined in the ApiService which returns an Observable. We simply subscribe to this observable in order to send a GET request to our JSON endpoint and get the response data that we finally assign to the items array.

We call the fetchData() method in the ngOnInit() life-cycle event so it will be called once the AppComponent component is initialized.

Adding The Application UI

Our application UI will consist of a navigation bar and the skeleton of the page which will be created with Angular Material.

Before using an Angular Material component, you’ll need to import its module. Each Material component belongs to its own module.

Open the src/app/app.module.ts file and add the following imports:

/*...*/
import  MatToolbarModule  from  '@angular/material/toolbar';
import  MatCardModule  from  '@angular/material/card';
import  MatButtonModule  from  '@angular/material/button';

@NgModule(
declarations: [
AppComponent
],
imports: [
/*...*/
MatToolbarModule,
MatCardModule,
MatButtonModule
],
providers: [],
bootstrap: [AppComponent]
)
export  class  AppModule  

We import modules for toolbar, card and button components and we add them to the imports array of the AppModule.

Next, open the src/app/app.component.html file, delete what’s in there and add:

<mat-toolbar  color="primary">
<mat-toolbar-row>
<span>JS-jargon</span>
</mat-toolbar-row>
</mat-toolbar>
<main>
<mat-card *ngFor="let item of items">
<mat-card-header>
<mat-card-title>item.name}</mat-card-title>
</mat-card-header>
<mat-card-content>
item.description}
</mat-card-content>
<mat-card-actions>
<a  mat-raised-button  href="item.url}"  color="primary">More</a>
</mat-card-actions>
</mat-card>
</main>

We use Material components to create the UI. The <mat-toolbar> component is used to create a Material toolbar and the <mat-card> component is used to create a Material card etc.

We iterate over the items array which gets populated by the fetchData() method when the component is initialized, and display items as Material cards. Each card contains the name, description and a link for more information (The link is styled as a Material button using the mat-raised-button directive).

This is a screenshot of the application:


Demo Application


Demo Application. (Large preview)

Building The Application For Production

Typically, when checking your application for PWA features you should first build it for production because most PWA features are not added in development. For example, you don’t want to have service workers and caching enabled in development since you will periodically need to update the files.

Let’s build the application for production using the following command:

$ ng build --prod

The production build will be available from the dist/pwademo folder. We can use a tool like http-server to serve it.

First, install http-server using the following command:

$ npm i -g http-server

You can then run it using the following command:

$ cd dist/pwademo
$ http-server -o

The -o option will automatically open the default browser in your system and navigate to the http://127.0.0.1:8080/ address where our web application is available.

Analyzing The Application Using Lighthouse

Let’s now analyze our application using Lighthouse. First, launch Chrome and visit our application address http://127.0.0.1:8080/.

Next, open Developer Tools or press Ctrl + Shift + I and click on the Audit panel.


Perform an audit


Perform an audit. (Large preview)

You preferably need to set the Emulation to Mobile instead of Desktop to emulate a mobile environment. Next, click on Perform an audit… blue button. You’ll have a dialog opened in which you need to choose the types of the audits you want to perform against your web application. Un-check all types but Progressive Web App and click the Run audit button.


Progressive Web App Audits


Progressive Web App Audits. (Large preview)

Wait for the Lighthouse to generate the report. This is a screenshot of the result at this stage:


Initial PWA Report


Initial Report. (Large preview)

Lighthouse performs a series of checks which validate the aspects of a Progressive Web App specified by the PWA Checklist.
We get an initial score of 36100 that’s because we have some audits passed.

Our application has 7 failed audits mainly related to Service Workers, Progressive Enhancement, HTTPS and Web App Manifest which are the core aspects of a PWA.

Registering A Service Worker

The first two failed audits (“Does not register a service worker” and “Does not respond with a 200 when offline”) are related to Service Workers and caching. So what’s a service worker?

A service worker is a feature that’s available on modern browsers which can be used as a network proxy that lets your application intercept network requests to cache assets and data. This could be used for implementing PWA features such as offline support and Push notifications etc.

To pass these audits we simply need to register a service worker and use it to cache files locally. When offline, the SW should return the locally cached version of the file. We’ll see a bit later how to add that with one CLI command.

Recommended reading: Making A Service Worker: A Case Study

Progressive Enhancement

The third failed audit (“Does not provide fallback content when JavaScript is not available”) is related to Progressive Enhancement which is an essential aspect of a PWA and It simply refers to the capability of PWAs to run on different browsers but provide advanced features if they’re available. One simple example of PE is the use of the <noscript> HTML tag that informs users of the need to enable JavaScript to run the application in case It’s not enabled:

<noscript>
Please enable JavaScript to run this application.
</noscript>

HTTPS

The fourth failed audit (“Does not redirect HTTP traffic to HTTPS”) is related to HTTPS which is also a core aspect of PWAs (service workers can be only served from secure origins, except for localhost). The “Uses HTTPS” audit itself is considered as passed by Lighthouse since we’re auditing localhost but once you use an actual host you need a SSL certificate. You can get a free SSL certificate from different services such as Let’s Encrypt, Cloudflare, Firebase or Netlify etc.

The Web App Manifest

The three failed audits (“User will not be prompted to Install the Web App”, “Is not configured for a custom Splash Screen” and “Address bar does not match brand colors”) are related to a missing Web App Manifest which is a file in JSON format that provides the name, description, icons and other information required by a PWA. It lets users install the web app on the home screen just like native apps without going through an app store.

You need to provide a web app manifest and reference it from the index.html file using a <link> tag with rel property set to manifest. We’ll see next how we can do that automatically with one CLI command.

Implementing PWA Features

Angular CLI v6 allows you to quickly add PWA features to an existing Angular application. You can turn your application into a PWA by simply running the following command in your terminal from the root of the project:

$ ng add @angular/pwa

The command automatically adds PWA features to our Angular application, such as:

  • A manifest.json file,
  • Different sizes of icons in the src/assets/icons folder,
  • The ngsw-worker.js service worker.

Open the dist/ folder which contains the production build. You’ll find various files but let’s concentrate on the files related to PWA features that we mentioned above:

A manifest.json file was added with the following content:


    "name": "pwademo",
    "short_name": "pwademo",
    "theme_color": "#1976d2",
    "background_color": "#fafafa",
    "display": "standalone",
    "scope": "/",
    "start_url": "/",
    "icons": [
        
        "src": "assets/icons/icon-72x72.png",
        "sizes": "72x72",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-96x96.png",
        "sizes": "96x96",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-128x128.png",
        "sizes": "128x128",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-144x144.png",
        "sizes": "144x144",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-152x152.png",
        "sizes": "152x152",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-384x384.png",
        "sizes": "384x384",
        "type": "image/png"
    ,
    
        "src": "assets/icons/icon-512x512.png",
        "sizes": "512x512",
        "type": "image/png"
    
    ]
}

As you can see, the added manifest.json file has all the information required by a PWA such as the name, description and start_url etc.


Angular project structure


Angular project structure. (Large preview)

The manifest.json file, links to icons with different sizes, that were also added automatically in the assets/icons folder. You will, of course, need to change those icons with your own once you are ready to build the final version of your PWA.


Angular project structure


Angular project structure. (Large preview)

In the index.html file, the manifest.json file is referenced using:

<link  rel="manifest"  href="manifest.json">

The ngsw-worker.js file, was also automatically added, which contains the service worker. The code to install this service worker is automatically inserted in the src/app/app.module.ts file:

...
import  ServiceWorkerModule  from  '@angular/service-worker';

@NgModule(
declarations: [
AppComponent
],

imports: [
...
ServiceWorkerModule.register('/ngsw-worker.js',  enabled:  environment.production )
],

The @angular/service-worker is installed by the ng add command and added as a dependency to pwademo/package.json:

"dependencies": 
...
"@angular/service-worker": "^6.1.0"

The service worker build support is also enabled in the CLI. In the angular.json file a "serviceWorker": true configuration option is added.

In the index.html file a meta tag for theme-color with a value of #1976d2 is added (It also corresponds to the theme_color value in the manifest.json file):

<meta  name="theme-color"  content="#1976d2">

The theme color tells the browser what color to tint UI elements such as the address bar.

Adding the theme color to both the index.html and manifest.json files fixes the Address Bar Matches Brand Colors audit.

The Service Worker Configuration File

Another file src/ngsw-config.json is added to the project but It’s not a required file for PWAs. It’s a configuration file which allows you to specify which files and data URLs the Angular service worker should cache and how it should update the cached files and data. You can find all details about this file from the official docs.

Note: As of this writing, with the latest 6.1.3 previous ng add @angular/pwa command will fail with this error: Path “/ngsw-config.json” already exists so for now the solution is to downgrade @angular/cli and @angular/pwa to version 6.0.8.

Simply run the following commands in your project:

$ npm i @angular/cli@6.0.8
$ ng i @angular/pwa@6.0.8
$ ng add @angular/pwa

Now let’s re-run the audits against our local PWA hosted locally. This is the new PWA score:


Initial PWA Report


PWA Report. (Large preview)

The Angular CLI doesn’t automatically add the JavaScript fallback code we mentioned in the Progressive Enhancement section so open the src/index.html file and add it:

<noscript>
Please enable JavaScript to run this application.
</noscript>

Next, rebuild your application and re-run the audits. This is the result now:


Initial PWA Report


PWA Report. (Large preview)

We have only one failed audit which is related to HTTPS redirect. We need to host the application and configure HTTP to HTTPS redirect.

Let’s now run the audits against a hosted and secured version of our PWA.


PWA Final Report


PWA Final Report. (Large preview)

We get a score of 100100 which means we’ve successfully implemented all core tenets of PWAs.

You can get the final code of this demo PWA from this GitHub repository.

Conclusion

In this tutorial, we’ve built a simple Angular application and have turned it into a PWA using Angular CLI. We used Google’s Lighthouse to audit our application for PWA features and explained various core tenets of PWAs such as Service Workers for adding offline support and push notifications. The Web Manifest file for enabling add-to-home-screen and splash screen features, Progressive Enhancement as well as HTTPS .

You may also need to manually check for other items highlighted (under the “Additional items to manually check” section) but not automatically checked by Lighthouse. These checks are required by the baseline PWA Checklist by Google. They do not affect the PWA score but it’s important that you verify them manually. For example, you need to make sure your site works cross-browser and that each page has a URL which is important for the purpose of shareability on social media.

Since PWAs are also about other aspects such as better perceived performance and accessibility, you can also use Lighthouse for auditing your PWA (or any general website) for these aspects and improve it as needed.

Smashing Editorial
(rb, ra, yk, il)


View this article:  

Building A PWA Using Angular 6

Thumbnail

Building A Room Detector For IoT Devices On Mac OS




Building A Room Detector For IoT Devices On Mac OS

Alvin Wan



Knowing which room you’re in enables various IoT applications — from turning on the light to changing TV channels. So, how can we detect the moment you and your phone are in the kitchen, or bedroom, or living room? With today’s commodity hardware, there are a myriad of possibilities:

One solution is to equip each room with a bluetooth device. Once your phone is within range of a bluetooth device, your phone will know which room it is, based on the bluetooth device. However, maintaining an array of Bluetooth devices is significant overhead — from replacing batteries to replacing dysfunctional devices. Additionally, proximity to the Bluetooth device is not always the answer: if you’re in the living room, by the wall shared with the kitchen, your kitchen appliances should not start churning out food.

Another, albeit impractical, solution is to use GPS. However, keep in mind hat GPS works poorly indoors in which the multitude of walls, other signals, and other obstacles wreak havoc on GPS’s precision.

Our approach instead is to leverage all in-range WiFi networks — even the ones your phone is not connected to. Here is how: consider the strength of WiFi A in the kitchen; say it is 5. Since there is a wall between the kitchen and the bedroom, we can reasonably expect the strength of WiFi A in the bedroom to differ; say it is 2. We can exploit this difference to predict which room we’re in. What’s more: WiFi network B from our neighbor can only be detected from the living room but is effectively invisible from the kitchen. That makes prediction even easier. In sum, the list of all in-range WiFi gives us plentiful information.

This method has the distinct advantages of:

  1. not requiring more hardware;
  2. relying on more stable signals like WiFi;
  3. working well where other techniques such as GPS are weak.

The more walls the better, as the more disparate the WiFi network strengths, the easier the rooms are to classify. You will build a simple desktop app that collects data, learns from the data, and predicts which room you’re in at any given time.

Further Reading on SmashingMag:

Prerequisites

For this tutorial, you will need a Mac OSX. Whereas the code can apply to any platform, we will only provide dependency installation instructions for Mac.

Step 0: Setup Work Environment

Your desktop app will be written in NodeJS. However, to leverage more efficient computational libraries like numpy, the training and prediction code will be written in Python. To start, we will setup your environments and install dependencies. Create a new directory to house your project.

mkdir ~/riot

Navigate into the directory.

cd ~/riot

Use pip to install Python’s default virtual environment manager.

sudo pip install virtualenv

Create a Python3.6 virtual environment named riot.

virtualenv riot --python=python3.6

Activate the virtual environment.

source riot/bin/activate

Your prompt is now preceded by (riot). This indicates we have successfully entered the virtual environment. Install the following packages using pip:

  • numpy: An efficient, linear algebra library
  • scipy: A scientific computing library that implements popular machine learning models
pip install numpy==1.14.3 scipy
==1.1.0

With the working directory setup, we will start with a desktop app that records all WiFi networks in-range. These recordings will constitute training data for your machine learning model. Once we have data on hand, you will write a least squares classifier, trained on the WiFi signals collected earlier. Finally, we will use the least squares model to predict the room you’re in, based on the WiFi networks in range.

Step 1: Initial Desktop Application

In this step, we will create a new desktop application using Electron JS. To begin, we will instead the Node package manager npm and a download utility wget.

brew install npm wget

To begin, we will create a new Node project.

npm init

This prompts you for the package name and then the version number. Hit ENTER to accept the default name of riot and default version of 1.0.0.

package name: (riot)
version: (1.0.0)

This prompts you for a project description. Add any non-empty description you would like. Below, the description is room detector

description: room detector

This prompts you for the entry point, or the main file to run the project from. Enter app.js.

entry point: (index.js) app.js

This prompts you for the test command and git repository. Hit ENTER to skip these fields for now.

test command:
git repository:

This prompts you for keywords and author. Fill in any values you would like. Below, we use iot, wifi for keywords and use John Doe for the author.

keywords: iot,wifi
author: John Doe

This prompts you for the license. Hit ENTER to accept the default value of ISC.

license: (ISC)

At this point, npm will prompt you with a summary of information so far. Your output should be similar to the following.


  "name": "riot",
  "version": "1.0.0",
  "description": "room detector",
  "main": "app.js",
  "scripts": 
    "test": "echo "Error: no test specified" && exit 1"
  ,
  "keywords": [
    "iot",
    "wifi"
  ],
  "author": "John Doe",
  "license": "ISC"
}

Hit ENTER to accept. npm then produces a package.json. List all files to double-check.

ls

This will output the only file in this directory, along with the virtual environment folder.

package.json
riot

Install NodeJS dependencies for our project.

npm install electron --global  # makes electron binary accessible globally
npm install node-wifi --save

Start with main.js from Electron Quick Start, by downloading the file, using the below. The following -O argument renames main.js to app.js.

wget https://raw.githubusercontent.com/electron/electron-quick-start/master/main.js -O app.js

Open app.js in nano or your favorite text editor.

nano app.js

On line 12, change index.html to static/index.html, as we will create a directory static to contain all HTML templates.

function createWindow () 
  // Create the browser window.
  win = new BrowserWindow(width: 1200, height: 800)

  // and load the index.html of the app.
  win.loadFile('static/index.html')

  // Open the DevTools.

Save your changes and exit the editor. Your file should match the source code of the app.js file. Now create a new directory to house our HTML templates.

mkdir static

Download a stylesheet created for this project.

wget https://raw.githubusercontent.com/alvinwan/riot/master/static/style.css?token=AB-ObfDtD46ANlqrObDanckTQJ2Q1Pyuks5bf79PwA%3D%3D -O static/style.css

Open static/index.html in nano or your favorite text editor. Start with the standard HTML structure.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Room Detector</title>
    </head>
    <body>
      <main>
      </main>
    </body>
  </html>

Right after the title, link the Montserrat font linked by Google Fonts and stylesheet.

<title>Riot | Room Detector</title>
  <!-- start new code -->
  <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
  <link href="style.css" rel="stylesheet">
  <!-- end new code -->
</head>

Between the main tags, add a slot for the predicted room name.

<main>
  <!-- start new code -->
  <p class="text">I believe you’re in the</p>
  <h1 class="title" id="predicted-room-name">(I dunno)</h1>
  <!-- end new code -->
</main>

Your script should now match the following exactly. Exit the editor.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Room Detector</title>
      <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
      <link href="style.css" rel="stylesheet">
    </head>
    <body>
      <main>
        <p class="text">I believe you’re in the</p>
        <h1 class="title" id="predicted-room-name">(I dunno)</h1>
      </main>
    </body>
  </html>

Now, amend the package file to contain a start command.

nano package.json

Right after line 7, add a start command that’s aliased to electron .. Make sure to add a comma to the end of the previous line.

"scripts": 
  "test": "echo "Error: no test specified" && exit 1",
  "start": "electron ."
,

Save and exit. You are now ready to launch your desktop app in Electron JS. Use npm to launch your application.

npm start

Your desktop application should match the following.


home page with button


Home page with “Add New Room” button available (Large preview)

This completes your starting desktop app. To exit, navigate back to your terminal and CTRL+C. In the next step, we will record wifi networks, and make the recording utility accessible through the desktop application UI.

Step 2: Record WiFi Networks

In this step, you will write a NodeJS script that records the strength and frequency of all in-range wifi networks. Create a directory for your scripts.

mkdir scripts

Open scripts/observe.js in nano or your favorite text editor.

nano scripts/observe.js

Import a NodeJS wifi utility and the filesystem object.

var wifi = require('node-wifi');
var fs = require('fs');

Define a record function that accepts a completion handler.

/**
 * Uses a recursive function for repeated scans, since scans are asynchronous.
 */
function record(n, completion, hook) 

Inside the new function, initialize the wifi utility. Set iface to null to initialize to a random wifi interface, as this value is currently irrelevant.

function record(n, completion, hook) 
    wifi.init(
        iface : null
    );
}

Define an array to contain your samples. Samples are training data we will use for our model. The samples in this particular tutorial are lists of in-range wifi networks and their associated strengths, frequencies, names etc.

function record(n, completion, hook) 
    ...
    samples = []

Define a recursive function startScan, which will asynchronously initiate wifi scans. Upon completion, the asynchronous wifi scan will then recursively invoke startScan.

function record(n, completion, hook) 
  ...
  function startScan(i) 
    wifi.scan(function(err, networks) 
    );
  }
  startScan(n);
}

In the wifi.scan callback, check for errors or empty lists of networks and restart the scan if so.

wifi.scan(function(err, networks) 
  if (err 
});

Add the recursive function’s base case, which invokes the completion handler.

wifi.scan(function(err, networks) 
  ...
  if (i <= 0) 
    return completion(samples: samples);
  }
});

Output a progress update, append to the list of samples, and make the recursive call.

wifi.scan(function(err, networks) 
  ...
  hook(n-i+1, networks);
  samples.push(networks);
  startScan(i-1);
);

At the end of your file, invoke the record function with a callback that saves samples to a file on disk.

function record(completion) 
  ...


function cli() 
  record(1, function(data) 
    fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() );
  }, function(i, networks) 
    console.log(" * [INFO] Collected sample " + (21-i) + " with " + networks.length + " networks");
  )
}

cli();

Double check that your file matches the following:

var wifi = require('node-wifi');
var fs = require('fs');

/**
 * Uses a recursive function for repeated scans, since scans are asynchronous.
 */
function record(n, completion, hook) 
  wifi.init(
      iface : null // network interface, choose a random wifi interface if set to null
  );

  samples = []
  function startScan(i) 
    wifi.scan(function(err, networks) 
        if (err 
        if (i <= 0) 
          return completion(samples: samples);
        }
        hook(n-i+1, networks);
        samples.push(networks);
        startScan(i-1);
    });
  }

  startScan(n);
}

function cli() 
    record(1, function(data) 
        fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() );
    }, function(i, networks) 
        console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks");
    )
}

cli();

Save and exit. Run the script.

node scripts/observe.js

Your output will match the following, with variable numbers of networks.

 * [INFO] Collected sample 1 with 39 networks

Examine the samples that were just collected. Pipe to json_pp to pretty print the JSON and pipe to head to view the first 16 lines.

cat samples.json | json_pp | head -16

The below is example output for a 2.4 GHz network.


  "samples": [
    [
      
        "mac": "64:0f:28:79:9a:29",
        "bssid": "64:0f:28:79:9a:29",
        "ssid": "SMASHINGMAGAZINEROCKS",
         "channel": 4,
         "frequency": 2427,
          "signal_level": "-91",
          "security": "WPA WPA2",
          "security_flags": [
           "(PSK/AES,TKIP/TKIP)",
          "(PSK/AES,TKIP/TKIP)"
        ]
      ,

This concludes your NodeJS wifi-scanning script. This allows us to view all in-range WiFi networks. In the next step, you will make this script accessible from the desktop app.

Step 3: Connect Scan Script To Desktop App

In this step, you will first add a button to the desktop app to trigger the script with. Then, you will update the desktop app UI with the script’s progress.

Open static/index.html.

nano static/index.html

Insert the “Add” button, as shown below.

<h1 class="title" id="predicted-room-name">(I dunno)</h1>
        <!-- start new code -->
        <div class="buttons">
            <a href="add.html" class="button">Add new room</a>
        </div>
        <!-- end new code -->
    </main>

Save and exit. Open static/add.html.

nano static/add.html

Paste the following content.

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Riot | Add New Room</title>
      <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
      <link href="style.css" rel="stylesheet">
    </head>
    <body>
      <main>
        <h1 class="title" id="add-title">0</h1>
        <p class="subtitle">of <span>20</span> samples needed. Feel free to move around the room.</p>
        <input type="text" id="add-room-name" class="text-field" placeholder="(room name)">
        <div class="buttons">
          <a href="#" id="start-recording" class="button">Start recording</a>
          <a href="index.html" class="button light">Cancel</a>
        </div>
        <p class="text" id="add-status" style="display:none"></p>
      </main>
      <script>
        require('../scripts/observe.js')
      </script>
    </body>
  </html>

Save and exit. Reopen scripts/observe.js.

nano scripts/observe.js

Beneath the cli function, define a new ui function.

function cli() 
    ...


// start new code
function ui() 

// end new code

cli();

Update the desktop app status to indicate the function has started running.

function ui() 
  var room_name = document.querySelector('#add-room-name').value;
  var status = document.querySelector('#add-status');
  var number = document.querySelector('#add-title');
  status.style.display = "block"
  status.innerHTML = "Listening for wifi..."

Partition the data into training and validation data sets.

function ui() 
  ...
  function completion(data) 
    train_data = samples: data['samples'].slice(0, 15)
    test_data = samples: data['samples'].slice(15)
    var train_json = JSON.stringify(train_data);
    var test_json = JSON.stringify(test_data);
  }
}

Still within the completion callback, write both datasets to disk.

function ui() 
  ...
  function completion(data) 
    ...
    fs.writeFile('data/' + room_name + '_train.json', train_json, 'utf8', function() );
    fs.writeFile('data/' + room_name + '_test.json', test_json, 'utf8', function() {});
    console.log(" * [INFO] Done")
    status.innerHTML = "Done."
  }
}

Invoke record with the appropriate callbacks to record 20 samples and save the samples to disk.

function ui() 
  ...
  function completion(data) 
    ...
  
  record(20, completion, function(i, networks) 
    number.innerHTML = i
    console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks")
  )
}

Finally, invoke the cli and ui functions where appropriate. Start by deleting the cli(); call at the bottom of the file.

function ui() 
    ...


cli();  // remove me

Check if the document object is globally accessible. If not, the script is being run from the command line. In this case, invoke the cli function. If it is, the script is loaded from within the desktop app. In this case, bind the click listener to the ui function.

if (typeof document == 'undefined') 
    cli();
 else 
    document.querySelector('#start-recording').addEventListener('click', ui)

Save and exit. Create a directory to hold our data.

mkdir data

Launch the desktop app.

npm start

You will see the following homepage. Click on “Add room”.




(Large preview)

You will see the following form. Type in a name for the room. Remember this name, as we will use this later on. Our example will be bedroom.


Add New Room page


“Add New Room” page on load (Large preview)

Click “Start recording,” and you will see the following status “Listening for wifi…”.


starting recording


“Add New Room” starting recording (Large Preview)

Once all 20 samples are recorded, your app will match the following. The status will read “Done.”




“Add New Room” page after recording is complete (Large preview)

Click on the misnamed “Cancel” to return to the homepage, which matches the following.


finished recording


“Add New Room” page after recording is complete (Large preview)

We can now scan wifi networks from the desktop UI, which will save all recorded samples to files on disk. Next, we will train an out-of-box machine learning algorithm-least squares on the data you have collected.

Step 4: Write Python Training Script

In this step, we will write a training script in Python. Create a directory for your training utilities.

mkdir model

Open model/train.py

nano model/train.py

At the top of your file, import the numpy computational library and scipy for its least squares model.

import numpy as np
from scipy.linalg import lstsq
import json
import sys

The next three utilities will handle loading and setting up data from the files on disk. Start by adding a utility function that flattens nested lists. You will use this to flatten a list of list of samples.

import sys

def flatten(list_of_lists):
    """Flatten a list of lists to make a list.
    >>> flatten([[1], [2], [3, 4]])
    [1, 2, 3, 4]
    """
    return sum(list_of_lists, [])

Add a second utility that loads samples from the specified files. This method abstracts away the fact that samples are spread out across multiple files, returning just a single generator for all samples. For each of the samples, the label is the index of the file. e.g., If you call get_all_samples('a.json', 'b.json'), all samples in a.json will have label 0 and all samples in b.json will have label 1.

def get_all_samples(paths):
  """Load all samples from JSON files."""
  for label, path in enumerate(paths):
  with open(path) as f:
    for sample in json.load(f)['samples']:
      signal_levels = [
        network['signal_level'].replace('RSSI', '') or 0
        for network in sample]
      yield [network['mac'] for network in sample], signal_levels, label

Next, add a utility that encodes the samples using a bag-of-words-esque model. Here is an example: Assume we collect two samples.

  1. wifi network A at strength 10 and wifi network B at strength 15
  2. wifi network B at strength 20 and wifi network C at strength 25.

This function will produce a list of three numbers for each of the samples: the first value is the strength of wifi network A, the second for network B, and the third for C. In effect, the format is [A, B, C].

  1. [10, 15, 0]
  2. [0, 20, 25]
def bag_of_words(all_networks, all_strengths, ordering):
  """Apply bag-of-words encoding to categorical variables.

  >>> samples = bag_of_words(
  ...     [['a', 'b'], ['b', 'c'], ['a', 'c']],
  ...     [[1, 2], [2, 3], [1, 3]],
  ...     ['a', 'b', 'c'])
  >>> next(samples)
  [1, 2, 0]
  >>> next(samples)
  [0, 2, 3]
  """
  for networks, strengths in zip(all_networks, all_strengths):
    yield [strengths[networks.index(network)]
      if network in networks else 0
      for network in ordering]

Using all three utilities above, we synthesize a collection of samples and their labels. Gather all samples and labels using get_all_samples. Define a consistent format ordering to one-hot encode all samples, then apply one_hot encoding to samples. Finally, construct the data and label matrices X and Y respectively.

def create_dataset(classpaths, ordering=None):
  """Create dataset from a list of paths to JSON files."""
  networks, strengths, labels = zip(*get_all_samples(classpaths))
  if ordering is None:
    ordering = list(sorted(set(flatten(networks))))
  X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64)
  Y = np.array(list(labels)).astype(np.int)
  return X, Y, ordering

These functions complete the data pipeline. Next, we abstract away model prediction and evaluation. Start by defining the prediction method. The first function normalizes our model outputs, so that the sum of all values totals to 1 and that all values are non-negative; this ensures that the output is a valid probability distribution. The second evaluates the model.

def softmax(x):
  """Convert one-hotted outputs into probability distribution"""
  x = np.exp(x)
  return x / np.sum(x)


def predict(X, w):
  """Predict using model parameters"""
  return np.argmax(softmax(X.dot(w)), axis=1)

Next, evaluate the model’s accuracy. The first line runs prediction using the model. The second counts the numbers of times both predicted and true values agree, then normalizes by the total number of samples.

def evaluate(X, Y, w):
  """Evaluate model w on samples X and labels Y."""
  Y_pred = predict(X, w)
  accuracy = (Y == Y_pred).sum() / X.shape[0]
  return accuracy

This concludes our prediction and evaluation utilities. After these utilities, define a main function that will collect the dataset, train, and evaluate. Start by reading the list of arguments from the command line sys.argv; these are the rooms to include in training. Then create a large dataset from all of the specified rooms.

def main():
  classes = sys.argv[1:]

  train_paths = sorted(['data/{}_train.json'.format(name) for name in classes])
  test_paths = sorted(['data/{}_test.json'.format(name) for name in classes])
  X_train, Y_train, ordering = create_dataset(train_paths)
  X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)

Apply one-hot encoding to the labels. A one-hot encoding is similar to the bag-of-words model above; we use this encoding to handle categorical variables. Say we have 3 possible labels. Instead of labelling 1, 2, or 3, we label the data with [1, 0, 0], [0, 1, 0], or [0, 0, 1]. For this tutorial, we will spare the explanation for why one-hot encoding is important. Train the model, and evaluate on both the train and validation sets.

def main():
  ...
  X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)
  
  Y_train_oh = np.eye(len(classes))[Y_train]
  w, _, _, _ = lstsq(X_train, Y_train_oh)
  train_accuracy = evaluate(X_train, Y_train, w)
  test_accuracy = evaluate(X_test, Y_test, w)

Print both accuracies, and save the model to disk.

def main():
  ...
  print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, test_accuracy*100))
  np.save('w.npy', w)
  np.save('ordering.npy', np.array(ordering))
  sys.stdout.flush()

At the end of the file, run the main function.

if __name__ == '__main__':
  main()

Save and exit. Double check that your file matches the following:

import numpy as np
from scipy.linalg import lstsq
import json
import sys


def flatten(list_of_lists):
    """Flatten a list of lists to make a list.
    >>> flatten([[1], [2], [3, 4]])
    [1, 2, 3, 4]
    """
    return sum(list_of_lists, [])


def get_all_samples(paths):
    """Load all samples from JSON files."""
    for label, path in enumerate(paths):
        with open(path) as f:
            for sample in json.load(f)['samples']:
                signal_levels = [
                    network['signal_level'].replace('RSSI', '') or 0
                    for network in sample]
                yield [network['mac'] for network in sample], signal_levels, label


def bag_of_words(all_networks, all_strengths, ordering):
    """Apply bag-of-words encoding to categorical variables.
    >>> samples = bag_of_words(
    ...     [['a', 'b'], ['b', 'c'], ['a', 'c']],
    ...     [[1, 2], [2, 3], [1, 3]],
    ...     ['a', 'b', 'c'])
    >>> next(samples)
    [1, 2, 0]
    >>> next(samples)
    [0, 2, 3]
    """
    for networks, strengths in zip(all_networks, all_strengths):
        yield [int(strengths[networks.index(network)])
            if network in networks else 0
            for network in ordering]


def create_dataset(classpaths, ordering=None):
    """Create dataset from a list of paths to JSON files."""
    networks, strengths, labels = zip(*get_all_samples(classpaths))
    if ordering is None:
        ordering = list(sorted(set(flatten(networks))))
    X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64)
    Y = np.array(list(labels)).astype(np.int)
    return X, Y, ordering


def softmax(x):
    """Convert one-hotted outputs into probability distribution"""
    x = np.exp(x)
    return x / np.sum(x)


def predict(X, w):
    """Predict using model parameters"""
    return np.argmax(softmax(X.dot(w)), axis=1)


def evaluate(X, Y, w):
    """Evaluate model w on samples X and labels Y."""
    Y_pred = predict(X, w)
    accuracy = (Y == Y_pred).sum() / X.shape[0]
    return accuracy


def main():
    classes = sys.argv[1:]

    train_paths = sorted(['data/{}_train.json'.format(name) for name in classes])
    test_paths = sorted(['data/{}_test.json'.format(name) for name in classes])
    X_train, Y_train, ordering = create_dataset(train_paths)
    X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)

    Y_train_oh = np.eye(len(classes))[Y_train]
    w, _, _, _ = lstsq(X_train, Y_train_oh)
    train_accuracy = evaluate(X_train, Y_train, w)
    validation_accuracy = evaluate(X_test, Y_test, w)

    print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, validation_accuracy*100))
    np.save('w.npy', w)
    np.save('ordering.npy', np.array(ordering))
    sys.stdout.flush()


if __name__ == '__main__':
    main()

Save and exit. Recall the room name used above when recording the 20 samples. Use that name instead of bedroom below. Our example is bedroom. We use -W ignore to ignore warnings from a LAPACK bug.

python -W ignore model/train.py bedroom

Since we’ve only collected training samples for one room, you should see 100% training and validation accuracies.

Train accuracy (100.0%), Validation accuracy (100.0%)

Next, we will link this training script to the desktop app.

In this step, we will automatically retrain the model whenever the user collects a new batch of samples. Open scripts/observe.js.

nano scripts/observe.js

Right after the fs import, import the child process spawner and utilities.

var fs = require('fs');
// start new code
const spawn = require("child_process").spawn;
var utils = require('./utils.js');

In the ui function, add the following call to retrain at the end of the completion handler.

function ui() 
  ...
  function completion() 
    ...
    retrain((data) => 
      var status = document.querySelector('#add-status');
      accuracies = data.toString().split('n')[0];
      status.innerHTML = "Retraining succeeded: " + accuracies
    );
  }
    ...
}

After the ui function, add the following retrain function. This spawns a child process that will run the python script. Upon completion, the process calls a completion handler. Upon failure, it will log the error message.

function ui() 
  ..


function retrain(completion) 
  var filenames = utils.get_filenames()
  const pythonProcess = spawn('python', ["./model/train.py"].concat(filenames));
  pythonProcess.stdout.on('data', completion);
  pythonProcess.stderr.on('data', (data) => 
    console.log(" * [ERROR] " + data.toString())
  )
}

Save and exit. Open scripts/utils.js.

nano scripts/utils.js

Add the following utility for fetching all datasets in data/.

var fs = require('fs');

module.exports = 
  get_filenames: get_filenames


function get_filenames() 
  filenames = new Set([]);
  fs.readdirSync("data/").forEach(function(filename) 
      filenames.add(filename.replace('_train', '').replace('_test', '').replace('.json', '' ))
  );
  filenames = Array.from(filenames.values())
  filenames.sort();
  filenames.splice(filenames.indexOf('.DS_Store'), 1)
  return filenames
}

Save and exit. For the conclusion of this step, physically move to a new location. There ideally should be a wall between your original location and your new location. The more barriers, the better your desktop app will work.

Once again, run your desktop app.

npm start

Just as before, run the training script. Click on “Add room”.


home page with button


Home page with “Add New Room” button available (Large preview)

Type in a room name that is different from your first room’s. We will use living room.


Add New Room page


“Add New Room” page on load (Large preview)

Click “Start recording,” and you will see the following status “Listening for wifi…”.




“Add New Room” starting recording for second room (Large preview)

Once all 20 samples are recorded, your app will match the following. The status will read “Done. Retraining model…”


finished recording 2


“Add New Room” page after recording for second room complete (Large preview)

In the next step, we will use this retrained model to predict the room you’re in, on the fly.

Step 6: Write Python Evaluation Script

In this step, we will load the pretrained model parameters, scan for wifi networks, and predict the room based on the scan.

Open model/eval.py.

nano model/eval.py

Import libraries used and defined in our last script.

import numpy as np
import sys
import json
import os
import json

from train import predict
from train import softmax
from train import create_dataset
from train import evaluate

Define a utility to extract the names of all datasets. This function assumes that all datasets are stored in data/ as <dataset>_train.json and <dataset>_test.json.

from train import evaluate

def get_datasets():
  """Extract dataset names."""
  return sorted(list(path.split('_')[0] for path in os.listdir('./data')
    if '.DS' not in path))

Define the main function, and start by loading parameters saved from the training script.

def get_datasets():
  ...

def main():
  w = np.load('w.npy')
  ordering = np.load('ordering.npy')

Create the dataset and predict.

def main():
  ...
  classpaths = [sys.argv[1]]
  X, _, _ = create_dataset(classpaths, ordering)
  y = np.asscalar(predict(X, w))

Compute a confidence score based on the difference between the top two probabilities.

def main():
  ...
  sorted_y = sorted(softmax(X.dot(w)).flatten())
  confidence = 1
  if len(sorted_y) > 1:
    confidence = round(sorted_y[-1] - sorted_y[-2], 2)

Finally, extract the category and print the result. To conclude the script, invoke the main function.

def main()
  ...
  category = get_datasets()[y]
  print(json.dumps("category": category, "confidence": confidence))

if __name__ == '__main__':
  main()

Save and exit. Double check your code matches the following (source code):

import numpy as np
import sys
import json
import os
import json

from train import predict
from train import softmax
from train import create_dataset
from train import evaluate


def get_datasets():
    """Extract dataset names."""
    return sorted(list(path.split('_')[0] for path in os.listdir('./data')
        if '.DS' not in path))


def main():
    w = np.load('w.npy')
    ordering = np.load('ordering.npy')

    classpaths = [sys.argv[1]]
    X, _, _ = create_dataset(classpaths, ordering)
    y = np.asscalar(predict(X, w))

    sorted_y = sorted(softmax(X.dot(w)).flatten())
    confidence = 1
    if len(sorted_y) > 1:
        confidence = round(sorted_y[-1] - sorted_y[-2], 2)

    category = get_datasets()[y]
    print(json.dumps("category": category, "confidence": confidence))


if __name__ == '__main__':
    main()

Next, we will connect this evaluation script to the desktop app. The desktop app will continuously run wifi scans and update the UI with the predicted room.

Step 7: Connect Evaluation To Desktop App

In this step, we will update the UI with a “confidence” display. Then, the associated NodeJS script will continuously run scans and predictions, updating the UI accordingly.

Open static/index.html.

nano static/index.html

Add a line for confidence right after the title and before the buttons.

<h1 class="title" id="predicted-room-name">(I dunno)</h1>
<!-- start new code -->
<p class="subtitle">with <span id="predicted-confidence">0%</span> confidence</p>
<!-- end new code -->
<div class="buttons">

Right after main but before the end of the body, add a new script predict.js.

</main>
  <!-- start new code -->
  <script>
  require('../scripts/predict.js')
  </script>
  <!-- end new code -->
</body>

Save and exit. Open scripts/predict.js.

nano scripts/predict.js

Import the needed NodeJS utilities for the filesystem, utilities, and child process spawner.

var fs = require('fs');
var utils = require('./utils');
const spawn = require("child_process").spawn;

Define a predict function which invokes a separate node process to detect wifi networks and a separate Python process to predict the room.

function predict(completion) 
  const nodeProcess = spawn('node', ["scripts/observe.js"]);
  const pythonProcess = spawn('python', ["-W", "ignore", "./model/eval.py", "samples.json"]);

After both processes have spawned, add callbacks to the Python process for both successes and errors. The success callback logs information, invokes the completion callback, and updates the UI with the prediction and confidence. The error callback logs the error.

function predict(completion) 
  ...
  pythonProcess.stdout.on('data', (data) => 
    information = JSON.parse(data.toString());
    console.log(" * [INFO] Room '" + information.category + "' with confidence '" + information.confidence + "'")
    completion()

    if (typeof document != "undefined") 
      document.querySelector('#predicted-room-name').innerHTML = information.category
      document.querySelector('#predicted-confidence').innerHTML = information.confidence
    
  });
  pythonProcess.stderr.on('data', (data) => 
    console.log(data.toString());
  )
}

Define a main function to invoke the predict function recursively, forever.

function main() 
  f = function()  predict(f) 
  predict(f)
}

main();

One last time, open the desktop app to see the live prediction.

npm start

Approximately every second, a scan will be completed and the interface will be updated with the latest confidence and predicted room. Congratulations; you have completed a simple room detector based on all in-range WiFi networks.

demo
Recording 20 samples inside the room and another 20 out in the hallway. Upon walking back inside, the script correctly predicts “hallway” then “bedroom.” (Large preview)

Conclusion

In this tutorial, we created a solution using only your desktop to detect your location within a building. We built a simple desktop app using Electron JS and applied a simple machine learning method on all in-range WiFi networks. This paves the way for Internet-of-things applications without the need for arrays of devices that are costly to maintain (cost not in terms of money but in terms of time and development).

Note: You can see the source code in its entirety on Github.

With time, you may find that this least squares does not perform spectacularly in fact. Try finding two locations within a single room, or stand in doorways. Least squares will be large unable to distinguish between edge cases. Can we do better? It turns out that we can, and in future lessons, we will leverage other techniques and the fundamentals of machine learning to better performance. This tutorial serves as a quick test bed for experiments to come.

Smashing Editorial
(ra, il)


This article is from - 

Building A Room Detector For IoT Devices On Mac OS

Thumbnail

Tripwire Marketing: Lure in More Customers With 12 Slam-Dunk Ideas

tripwire marketing

You’re unhappy with your conversion rate. People just aren’t buying what you’re selling. The solution might lie in tripwire marketing. The term tripwire marketing might sound a little shady, like you’re trying to get one over on your customer. That’s not the case at all. Marketing and advertising experts have been using tripwire marketing for decades in one form or another, and it works just as well online as it does in brick-and-mortar stores. In fact, it’s even more effective because you can more easily stay in touch with the customer. What is tripwire marketing? And how does it work?…

The post Tripwire Marketing: Lure in More Customers With 12 Slam-Dunk Ideas appeared first on The Daily Egg.

Read the article:  

Tripwire Marketing: Lure in More Customers With 12 Slam-Dunk Ideas

Advanced CSS Layouts With Flexbox and CSS Grid

Full-day workshop • June 28th
This workshop is designed for designers and developers who already have a good working knowledge of HTML and CSS. We will cover a range of CSS methods for achieving layout, from those you are safe to use right now even if you need to support older version of Internet Explorer through to things that while still classed as experimental, are likely to ship in browsers in the coming months.

Link – 

Advanced CSS Layouts With Flexbox and CSS Grid

Thumbnail

Meet Smashing Book 6: New Frontiers In Web Design




Meet Smashing Book 6: New Frontiers In Web Design

Vitaly Friedman



Let’s make sense of the front-end and UX madness. Meet Smashing Book 6 with everything from design systems to accessible single-page apps, CSS Custom Properties, Grid, Service Workers, performance patterns, AR/VR, conversational UIs & responsive art direction. And you can add your name into the book, too. About the book ↓.

Smashing Book 6 is dedicated to the challenges and headaches that we are facing today, and how to resolve them. No chit-chat, no theory: only practical, useful advice applicable to your work right away.

Smashing Book 6

Book

$29 $39Print + eBook

Printed, quality hardcover. Free airmail shipping worldwide. Sep 2018.

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. First chapters are already available.

About The Book

With so much happening in front-end and UX these days, it’s getting really hard to stay afloat, isn’t it? New technologies and techniques appear almost daily, and so navigating through it all in regular projects is daunting and time-consuming. Frankly, we just don’t have time to afford betting on a wrong strategy. That’s why we created Smashing Book 6, our shiny new book that explores uncharted territories and seeks to establish a map for the brave new front-end world.

You know the drill: the book isn’t about tools; it’s about workflow, strategy and shortcuts to getting things done well. Respected members of the community explore how to build accessible single-page apps with React or Angular, how to use CSS Grid Layout, CSS Custom Properties and service workers as well as how to load assets on the web in times of HTTP/2 and bloated third-party scripts.

We’ll also examine how to make design systems work in real-life, how to design and build conversational interfaces, AR/VR, building for chatbots and watches and how to bring responsive art-direction back to the web.

Print shipping in late September 2018., hardcover + eBook. 432 pages. Written by Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmueller, Harry Roberts, Lyza D. Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Cannon and Vitaly Friedman. Pre-order the book today.

A look inside the book

Book

$29 $39Print + eBook

Printed, quality hardcover. Free airmail shipping worldwide. Shipping: Sept 2018.

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. First chapters are already available.

Why This Book Is For You

We worked hard to design the book in a way that it doesn’t become outdated quickly. That’s why it’s more focused on strategy rather than tooling. It’s about how we do things, but not necessarily the tools we all use to get there.

Table of Contents

The book contains 11 chapters, with topics ranging from design to front-end. Only practical advice, applicable to your work right away.

  • Making design systems work in real-life
    by Laura Elizabeth
  • Accessibility in times of SPAs
    by Marcy Sutton
  • Production-ready CSS Grid layouts
    by Rachel Andrew
  • Strategic guide to CSS Custom Properties
    by Mike Riethmueller
  • Taming performance bottlenecks
    by Harry Roberts
  • Building an advanced service worker
    by Lyza Gardner
  • Loading assets on the web
    by Yoav Weiss
  • Conversation interface design patterns
    by Adrian Zumbrunnen
  • Building chatbots and designing for watches
    by Greg Nudelman
  • Cross Reality and the web (AR/VR)
    by Ada Rose Cannon
  • Bringing personality back to the web
    by Vitaly Friedman
laura elizabeth
marcy sutton
rachel andrew
mike riethmuller
harry roberts
lyza d gardner
yoav weiss
adrian zumbrunnen
greg nudelman
ada rose edwards
vitaly friedman

From left to right: Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmuller, Harry Roberts, Lyza Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Edwards, and yours truly.

Book

$29 $39Print + eBook

Free airmail shipping worldwide. Sep 2018. Pre-order now and save 25%

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. You can start reading right away.

Download the Sample Chapter

To sneak a peek inside the book, you can download Vitaly’s chapter on bringing personality back to the web (PDF, ca. 18MB). Enjoy!

Goodie: New Frontiers In Web Design Wallpaper

To celebrate the pre-release of the book , Chiara Aliotta designed a set of mobile and desktop wallpapers for you to indulge in. Feel free to download it (ZIP, ca. 1.4MB).

New Frontiers In Web Design Wallpaper
Get ready for the new frontiers in web design with Chiara’s beautiful wallpaper.

About The Designer

Chiara AliottaThe cover was designed by the fantastic Chiara Aliotta. Chiara is an Italian award-winning designer with many years of experience as an art director and brand consultant. She founded the design studio Until Sunday and has directed the overall artistic look and feel of different tech companies and not-for-profit organizations around the world. We’re very happy that she gave Smashing Book 6 that special, magical touch.

Add Your Name To The Book

We kindly appreciate your support, and so as always, we invite you to add your name to the printed book: a double-page spread is reserved for the first 1.000 people. Space is limited, so please don’t wait too long!

Add your name to the book
A double-page spread is reserved for the first 1.000 readers. Add your name to the printed book.

Book

$29 $39Print + eBook

Free airmail shipping worldwide. Sep 2018. Pre-order now and save 25%

eBook

$14.90 $19Get the eBook

PDF, ePUB, Kindle. You can start reading right away.

Smashing Editorial
(cm)


Originally posted here:

Meet Smashing Book 6: New Frontiers In Web Design

Thumbnail

How To Deliver A Successful UX Project In The Healthcare Sector




How To Deliver A Successful UX Project In The Healthcare Sector

Sven Jungmann & Karolin Neubauer



A mid-career UX researcher was hired to understand the everyday needs, perceptions, and concerns of patients in a hospital in Berlin, Germany. She used rigorous observation and interviewing methods just like she teaches them to design thinking students at a nearby university. She returned with a handful of actionable insights that our product team found useful, somewhat at least.

However, we were surprised that her recommendations gravitated towards convenience issues such as “Patients want to know the food menu” or “Users struggle to remember who their doctors are.” Entirely missing were reports of physical and psychological complaints. We would at least have expected sleeping problems: Given that 80% of working Germans don’t sleep well and nearly 10% even appear to have severe sleeping disorders (link in German), why did no one mention it?

“We only see what we know.”

— Johann Wolfgang von Goethe (1749-1832)

If you are a UX researcher about to embark on a project with hospitalized patients and you want to avoid missing out on deep concerns and problems of users, then maybe this article can help you strengthen your awareness for particular challenges of clinical UX.

It is difficult to get in touch with real patients and get permission from clinical staff to access the right people. We are fortunate to have access to a network of more than 100 hospitals in Germany thanks to our sister Helios Kliniken GmbH, which is Europe’s largest private hospital provider. Our experience with clinical UX research taught us the importance of stressing that we cannot assume that patients bring up relevant concerns by themselves.

We describe three reasons we think are important to improve the quality and quantity of your findings. Generally speaking, this article emphasizes the need for UX practitioners to be mindful of their participants’ emotional and physical state. But we also discuss how we think UX researchers should prepare for and conduct a research project in the healthcare sector.

The Three B’s That Complicate UX Research In Hospitals

We’ve been thinking much about user research in hospital settings recently. Our new company, smart Helios, a digital health development firm, is a spin-off of Helios Kliniken, Europe’s largest private hospital chain. To inform our lean software development, we thoroughly embrace iterative empathetic observation and end-user interviews at each step of a development cycle (i.e., ideation, prototyping, and testing).

We learned that qualitative research in a hospital setting brings its challenges. We think it is worth considering them, especially those we discuss here, called the three B’s: Biases, Barriers, and Background.

Here’s an overview:

  1. Biases
    Psychological mechanisms can affect our patients’ thinking and hence diminish the results of our findings.
  2. Barriers to trust
    Patients rarely share intimate needs with a non-medical interviewer easily. This can create blind spots in our research.
  3. Background
    Internal and external factors such as wealth, socio-economic status, sanitation, education, and access to healthcare can influence our health as well as the care patients receive. The quality of care also depends on the hospital’s infrastructure and staff experience with a particular disease or procedure. These differences make it challenging to generalize findings.

We also discuss remedies that can help overcome these challenges and distill more valuable insights. They include:

  • Conducting a thorough and well-prepared interview.
  • Including healthcare providers in the process.
  • Drawing on quantitative data to guide some of the qualitative user research.

Biases: We Are Fantastic At Lying To Ourselves, Whether Or Not We’re Patients

Psychologists call them cognitive biases: they negatively affect how accurately we perceive and remember events or feelings, and we possess impressive amounts of them.

For example, people remember information better if it is more recent or salient. If you ask a patient about the initial appointment with her oncologist (this is rarely good news), don’t be surprised if she remembers only a quarter of it. Hospitalized patients are typically overwhelmed by the unfamiliar situation they’re in and often under stress and that influences their memory.

Hence it makes a difference when you ask them in their patient journey. Even in one day, patients face different problems that affect what’s top of mind at the moment you observe or interview them:

  • In the morning, when the painkillers have worn off during sleep, regaining control over physical pain is all that matters.
  • During the day, worries about upcoming procedures might dominate their thoughts or they could be focused on their hunger while they’re not allowed to eat or drink ahead of a diagnostic test.
  • In the evening, they might be afraid that they won’t be able to update their relatives appropriately.
  • At night, some struggle to sleep because of the hospital noise or their worries.

Takeaways:

  • Try interviewing users at different times of the day and different moments of their journey and take note of how findings vary.
  • Always get an orientation about where your users stand within their patient journey. Explore what happened in the previous hours or days and what diagnostics or treatments lie ahead of them.
  • Beware that our psyches possess a plethora of mechanisms to limit rationality and prevent past events from entering our conscious mind. You cannot control them all, but it improves your research if you notice them.

Barriers To Trust: Who Is Asking Matters, Too

It’s not just about how and when; it also matters who is asking. Even if a patient agrees to speak with us, what she shares will highly depend on how much she trusts us. One of us observed as a clinician how often his patients need to build up trust, sometimes over days until they ‘confess’ certain concerns. That’s especially true when problems have a psychological component (e.g., sleeping disorders) or are stigmatized (e.g., certain infectious diseases).

The more knowledgeable you are about health problems, the better you can steer interviews towards relevant issues. If you do this empathetically, your interviewees might find it easier to speak about them. Don’t get frustrated, however, if they don’t. Some people need a lot of trust, and there’s rarely a shortcut to earning it. In these cases, there’s something else you can do: include subjects who are not your target users but still have crucial insights in your interviews.

Take the nurse, for example. She might know from her previous night shift how many patients had trouble sleeping and who might agree to talk about it. The doctors will know which crucial questions they get asked frequently. And the housekeeping staff can share stories about the patients’ hygiene concerns. Listen closely to them: many of the staffs’ pain points likely hint to patients’ pain points, too. The more observers you allow to shed light on one subject, the better your chances to understand your patients’ experiences and the broader your perception of the system will be.

Takeaways:

  • Try to interview people involved in your users’ care.
  • Ask the clinical staff for guidance on which patients to ask about specific problems.
  • Even if patients are your primary users, make sure to ask health care providers, such as doctors, nurses, or therapists about common patient needs.
  • Schedule repeated interviews with patients if possible to build the trust necessary for sharing critical concerns.

The ‘living’ patient journey map.


A large, printed and ‘living’ patient journey helps to constantly challenge and refine assumptions. (Large preview)

Background: Mind The Worlds In And Around The Patients

Different patients have different needs. This is obvious, and part of the reason why UX researchers develop personas, conduct semi-structured interviews, focus group discussions, and observe subjects to explore the multiple realities of our participants. But there’s still a problem we can’t dismiss. Even within our hospitals inside Germany, people’s realities can differ greatly depending on their income, education, insurance, place of residence, etc.

What you discover in one hospital or region might not apply elsewhere. This can be a problem if you want to deploy your products at scale. If you have the opportunity, you should go the extra mile to conduct UX research in different hospitals to understand the needs and what drives adoption.

If some regions show poor sales, conduct field research in these regions to revisit your personas. In fact, don’t just challenge the personas, also explore the environment.

Here’s an example of why that matters: We developed a tool that relies on data from a hospital information system. This worked well in one clinic were staff was spread over different parts of the building, turning digital communication into an effective medium. In another hospital, however, the relevant people sat in the same room, making face-to-face communication significantly better than typing information into electronic records.

Takeaways:

  • Go beyond personas or archetypes and seek to understand the different realities between hospitals, wards, and regions.
  • Develop and constantly improve a rollout-playbook that lists local challenges and how you solved them to inform future expansions.

Sorry, Ignorance Is Not Bliss

Some UX researchers seem to believe that it is beneficial to enter an interview unprepared to avoid bias. Some shy away from acquiring relevant medical knowledge, thinking that (since they are not trained medical professionals) they won’t grasp the concepts anyway. Some feel that understanding the medical context of their interviewees’ situation will not add value to their research since they solely focus on the subjective experience of the disease.

But in evidence-based healthcare, conducting research on patients without previous peer-reviewed literature and guideline research is not only unprofessional but often even considered unethical. We should not fall prey to the illusion that ignorance frees us from bias.

The good news is that in healthcare, we are privileged to have a large body of well-conducted studies and systematic reviews available online, many of them free to access. PubMed is an excellent and open source, tutorials on how to use are abundantly available online (we think this is a good primer). Or if you have the budget, paid sites like UpToDate provide comprehensive disease reviews written both for professionals and for laypeople.

We know that UX researchers who are rightfully focused on ‘getting out of the building’ might not enjoy spending many hours on literature research but we are convinced that this will help you form better hypotheses and questions.

Moreover, if you start with clearly predefined research questions and seek answers in the scientific medical literature, you might save time and discover questions that you wouldn’t have thought of. For example, it is advised that individuals undergoing hip surgery should practice using crutches before the operation because it is already difficult enough even without the postoperative pain and swelling. This knowledge, obtained from literature research, could help move from more open questions, such as:

  • “How do patients prepare for a hip replacement surgery?” or
  • “What perceived needs to patients have ahead of hip surgery?”

To more concrete questions such as:

  • “What are the most important preparatory measures that many patients are currently unaware of?”

Takeaway:

  • Do a systematic literature review to inform your research.

Let The Data Guide You And You Guide The Data

To take this further, we’re also developing methods to use quantitative analytics and Deep Learning to guide our qualitative research. Our machine learning engineer just deployed AI to crawl the web for colon cancer blogs to identify hot topics that remained unmentioned in qualitative reviews. We defined “hot” as having many views, many comments, and many likes.

Or you can uncover semantic structures (see picture). These findings can then guide the UX researchers. Similarly, qualitative research can yield hypotheses that we can try to validate with passively collected data. For example, if you think that sleeping problems are common, you could (user consent provided) use your app to measure phone use at night as a proxy for sleeplessness.


Informing our researchers about hot topics in colon cancer using machine learning (topic modeling, credits Yuki Katoh and Ellen Hoeven)


Deploying topic modeling to find semantic structures in texts on colon cancer to inform our qualitative researchers. (Large preview)

Conclusion

Many of our suggestions are not new to well-trained UX researchers. We are aware of that. But in our experience, it is worth stressing the importance of mindfulness towards the three Bs: Biases, Barriers to trust, and Background. Here’s a summary of some of the recommendations to overcome the 3 Bs:

  • Prepare interviews with literature research on the topic (e.g., on Pubmed.gov).
  • Ask doctors which patients are suitable for interview.
  • Include those who care for your users, including nurses, therapists, and relatives.
  • Cooperate with the data scientists or web analysts in your team, if you have them.
  • Understand that it takes users time to build trust to tell you about some needs.
  • Explore how realities differ not only by personas, but also by regions and hospitals.
  • Stay aware that, no matter how much you try, the influence of the 3Bs can only be reduced, and not entirely removed.

We wish you well and thank you for making the world a healthier place.


The authors would like to thank their former colleague, Tim Leinert, for his thoughtful input to this piece.

Smashing Editorial
(cc, ra, yk, il)


See original article here: 

How To Deliver A Successful UX Project In The Healthcare Sector

Becoming A UX Leader




Becoming A UX Leader

Christopher Murphy



(This is a sponsored article.) In my previous article on Building UX Teams, I explored the rapidly growing need for UX teams as a result of the emergence of design as a wider business driver. As teams grow, so too does a need for leaders to nurture and guide them.

In my final article in this series on user experience design, I’ll explore the different characteristics of an effective UX leader, and provide some practical advice about growing into a leadership role.

I’ve worked in many organizations — both large and small, and in both the private and public sectors — and, from experience, leadership is a rare quality that is far from commonplace. Truly inspiring leaders are few and far between; if you’re fortunate to work with one, make every effort to learn from them.

Managers that have risen up the ranks don’t automatically become great leaders, and perhaps one of the biggest lessons I’ve learned is that truly inspirational leaders — those that inspire passion and commitment — aren’t as commonplace as you’d think.

A UX leader is truly a hybrid, perhaps more so than in many other — more traditional — businesses. A UX leader needs to encompass a wide range of skills:

  • Establishing, driving and articulating a vision;
  • Communicating across different teams, including design, research, writing, engineering, and business (no small undertaking!);
  • Acting as a champion for user-focused design;
  • Mapping design decisions to key performance indicators (KPIs), and vice-versa, so that success can be measured; and
  • Managing a team, ensuring all the team’s members are challenged and motivated.

UX leadership is not unlike being bi-lingual — or, more accurately, multi-lingual — and it’s a skill that requires dexterity so that nothing gets lost in translation.

This hybrid skill set can seem daunting, but — like anything — the attributes of leadership can be learned and developed. In my final article in this series of ten, I’ll explore what defines a leader and focus on the qualities and attributes needed to step up to this important role.

Undertaking A Skills Audit

Every leader is different, and every leader will be informed by the different experiences they have accumulated to date. There are, however, certain qualities and attributes that leaders tend to share in common.

Great leaders demonstrate self-awareness. They tend to have the maturity to have looked themselves in the mirror and identified aspects of their character that they may need to develop if they are to grow as leaders.

Having identified their strengths and weaknesses and pinpointing areas for improvement, they will have an idea of what they know and — equally important — what they don’t know. As Donald Rumsfeld famously put it:

“There are known knowns: there are things we know we know. We also know there are known unknowns: That is to say, we know there are some things we do not know. But there are also unknown unknowns: the things we don’t know we don’t know.”

Rumsfeld might have been talking about unknown unknowns in a conflict scenario, but his insight applies equally to the world of leadership. To grow as a leader, it’s important to widen your knowledge so that it addresses both:

  • The Known Unknowns
    Skills you know that you don’t know, which you can identify through a self-critical skills audit; and
  • The Unknown Unknowns
    Skills you don’t know you don’t know, which you can identify through inviting your peers to review your strengths and weaknesses.

In short, a skills audit will equip you with a roadmap that you can use as a map to plot a path from where you are now to where you want to be.


a map that you can use to plot a path from where you are now to where you want to be


Undertaking a skills audit will enable you to develop a map that you can use to plot a path from where you are now to where you want to be. (Large preview)

To become an effective leader is to embark upon a journey, identifying the gaps in your knowledge and — step by step — addressing these gaps so that you’re prepared for the leadership role ahead.

Identifying The Gaps In Your Knowledge

One way to identify the gaps in your knowledge is to undertake an honest and self-reflective ‘skills audit’ while making an effort to both learn about yourself and learn about the environment you are working within.

To become a UX leader, it’s critical to develop this self-awareness, identifying the knowledge you need to acquire by undertaking both self-assessments and peer assessments. With your gaps in knowledge identified, it’s possible to build a learning pathway to address these gaps.

In the introduction, I touched on a brief list of skills that an effective and well-equipped leader needs to develop. That list is just the tip of a very large iceberg. At the very least, a hybrid UX leader needs to equip themselves by:

  • Developing an awareness of context, expanding beyond the realms of design to encompass a broader business context;
  • Understanding and building relationships with a cross-section of team members;
  • Identifying outcomes and goals, establishing KPIs that will help to deliver these successfully;
  • Managing budgets, both soft and hard; and
  • Planning and mapping time, often across a diversified team.

These are just a handful of skills that an effective UX leader needs to develop. If you’re anything like me, hardly any of this was taught at art school, so you’ll need to learn these skills yourself. This article will help to point you in the right direction. I’ve also provided a list of required reading for reference to ensure you’re well covered.

A 360º Assessment

A 360º degree leadership assessment is a form of feedback for leaders. Drawn from the world of business, but equally applicable to the world of user experience, it is an excellent way to measure your effectiveness and influence as a leader.

Unlike a top-down appraisal, where a leader or manager appraises an employee underneath them in the hierarchy, a 360º assessment involves inviting your colleagues — at your peer level — to appraise you, highlighting your strengths and weaknesses.

This isn’t easy — and can lead to some uncomfortable home truths — but it can prove a critical tool in helping you to identify the qualities you need to work on. You might, for example, consider yourself an excellent listener only to discover that your colleagues feel like this is something you need to work on.

This willingness to put yourself under the spotlight, identify your weaknesses, address these, and develop yourself is one of the defining characteristics of leaders. Great leaders are always learning and they aren’t afraid to admit that fact.

A 360º assessment is a great way to uncover your ‘unknown unknowns’, i.e. the gaps in your knowledge that you aren’t aware of. With these discoveries in hand, it’s possible to build ‘a learning road-map’ that will allow you to develop the skills you need.

Build A Roadmap

With the gaps in your knowledge identified, it’s important to adopt some strategies to address these gaps. Great leaders understand that learning is a lifelong process and to transition into a leadership role will require — inevitably — the acquisition of new skills.

To develop as a leader, it’s important to address your knowledge gaps in a considered and systematic manner. By working back from your skills audit, identify what you need to work on and build a learning programme accordingly.

This will inevitably involve developing an understanding of different domains of knowledge, but that’s the leader’s path. The important thing is to take it step by step and, of course, to take that first step.

We are fortunate now to be working in an age in which we have an abundance of learning materials at our fingertips. We no longer need to enroll in a course at a university to learn; we can put together our own bespoke learning programmes.

We now have so many tools we can use, from paid resources like Skillshare which offers “access to a learning platform for personalized, on-demand learning,” to free resources like FutureLearn which offers the ability to “learn new skills, pursue your interests and advance your career.”

In short, you have everything you need to enhance your career just a click away.

It’s Not Just You

Great leaders understand that it’s not about the effort of individuals, working alone. It’s about the effort of individuals — working collectively. Looking back through the history of innovation, we can see that most (if not all) of the greatest breakthroughs were driven by teams that were motivated by inspirational leaders.

Thomas Edison didn’t invent the lightbulb alone; he had an ‘invention factory’ housed in a series of research laboratories. Similarly, when we consider the development of contemporary graphical user interfaces (GUIs), these emerged from the teamwork of Xerox PARC. The iPod was similarly conceived.

Great leaders understand that it’s not about them as individuals, but it’s about the teams they put together, which they motivate and orchestrate. They have the humility to build and lead teams that deliver towards the greater good.

This — above all — is one of the defining characteristics of a great leader: they prioritize and celebrate the team’s success over and above their own success.

It’s All About Teamwork

Truly great leaders understand the importance that teams play in delivering outcomes and goals. One of the most important roles a leader needs to undertake is to act as a lynchpin that sits at the heart of a team, identifying new and existing team members, nurturing them, and building them into a team that works effectively together.

A forward-thinking leader won’t just focus on the present, but will proactively develop a vision and long-term goals for future success. To deliver upon this vision of future success will involve both identifying potential new team members, but — just as importantly — developing existing team members. This involves opening your eyes to the different aspects of the business environment you occupy, getting to know your team, and identifying team members’ strengths and weaknesses.

As a UX leader, an important role you’ll play is helping others by mentoring and coaching them, ensuring they are equipped with the skills they need to grow. Again, this is where a truly selfless leader will put others first, in the knowledge that the stronger the team, the stronger the outcomes will be.

As a UX leader, you’ll also act as a champion for design within the wider business context. You’ll act as a bridge — and occasionally, a buffer — between the interface of business requirements and design requirements. Your role will be to champion the power of design and sell its benefits, always singing your team’s praises and — occasionally — fighting on their behalf (often without their awareness).

The Art Of Delegation

It’s possible to build a great UX team from the inside by developing existing team members, and an effective leader will use delegation as an effective development tool to enhance their team members’ capabilities.

Delegation isn’t just passing off the tasks you don’t want to do, it’s about empowering the different individuals in a team. A true leader understands this and spends the time required to learn how to delegate effectively.

Delegation is about education and expanding others’ skill sets, and it’s a powerful tool when used correctly. Effective delegation is a skill, one that you’ll need to acquire to step up into a UX leadership role.

When delegating a task to a team member, it’s important to explain to them why you’re delegating the task. As a leader, your role is to provide clear guidance and this involves explaining why you’ve chosen a team member for a task and how they will be supported, developed and rewarded for taking the task on.

This latter point is critical: All too often managers who lack leadership skills use delegation as a means to offload tasks and responsibility, unaware of the power of delegation. This is poor delegation and it’s ineffective leadership, though I imagine, sadly, we have all experienced it! An effective leader understands and strives to delegate effectively by:

  • defining the task, establishing the outcomes and goals;
  • identifying the appropriate individual or team to take the task on;
  • assessing the team member(s) ability and ascertaining any training needs;
  • explaining their reasoning, clearly outlining why they chose the individual or team;
  • stating the required results;
  • agreeing on realistic deadlines; and
  • providing feedback on completion of the task.

When outlined like this, it becomes clear that effective delegation is more than simply passing on a task you’re unwilling to undertake. Instead, it’s a powerful tool that an effective UX leader uses to enable their team members to take ownership of opportunities, whilst growing their skills and experience.

Give Success And Accept Failure

A great leader is selfless: they give credit for any successes to the team; and accept the responsibility for any failures alone.

A true leader gives success to the team, ensuring that — when there’s a win — the team is celebrated for its success. A true leader takes pleasure in celebrating the team’s win. When it comes to failure, however, a true leader steps up and takes responsibility. A mark of a truly great leader is this selflessness.

As a leader, you set the direction and nurture the team, so it stands to reason that, if things go wrong — which they often do — you’re willing to shoulder the responsibility. This understanding — that you need to give success and accept failure — is what separates great leaders from mediocre managers.

Poor managers will seek to ‘deflect the blame,’ looking for anyone but themselves to apportion responsibility to. Inspiring leaders are aware that, at the end of the day, they are responsible for the decisions made and outcomes reached; when things go wrong they accept responsibility.

If you’re to truly inspire others and lead them to achieve great things, it’s important to remember this distinction between managers and leaders. By giving success and accepting failure, you’ll breed intense loyalty in your team.

Lead By Example

Great leaders understand the importance of leading by example, acting as a beacon for others. To truly inspire a team, it helps to connect yourself with that team, and not isolate yourself. Rolling up your sleeves and pitching in, especially when deadlines are pressing, is a great way to demonstrate that you haven’t lost touch with the ‘front line.’

A great leader understands that success is — always — a team effort and that a motivated team will deliver far more than the sum of its parts.

As I’ve noted in my previous articles: If you’re ever the smartest person in a room, find another room. An effective leader has the confidence to surround themselves with other, smarter people.

Leadership isn’t about individual status or being seen to be the most talented. It’s about teamwork and getting the most out of a well-oiled machine of individuals working effectively together.

Get Out Of Your Silo

To lead effectively, it’s important to get out of your silo and to see the world as others do. This means getting to know all of the team, throughout the organization and at every level.

Leaders that isolate themselves — in their often luxurious corner offices — are, in my experience, poor leaders (if, indeed, they can be called leaders at all!). By distancing themselves from the individuals that make up an organization they run the very real risk of losing touch.

To lead, get out of your silo and acquaint yourself with the totality of your team and, if you’re considering a move into leadership, make it your primary task to explore all the facets of the business.

The Pieces Of The Jigsaw

To lead effectively, you need to have an understanding of others and their different skills. In my last article, Building a UX Team, I wrote about the idea of ‘T-shaped’ people — those that have a depth of skill in their field, along with the willingness and ability to collaborate across disciplines. Great leaders tend to be T-shaped, flourishing by seeing things from others’ perspectives.

Every organization — no matter how large or small — is like an elaborate jigsaw that is made up of many different interlocking parts. An effective leader is informed by an understanding of this context, they will have made the effort to see all of the pieces of the jigsaw. As a UX leader, you’ll need to familiarize yourself with a wide range of different teams, including design, research, writing, engineering, and business.

To lead effectively, it’s important to push outside of your comfort zone and learn about these different specialisms. Do so and you will ensure that you can communicate to these different stakeholders. At the risk of mixing metaphors, you will be the glue that holds the jigsaw together.

Sweat The Details

As Charles and Ray Eames put it:

“The details aren’t the details, they make the product.”

Great leaders understand this: they set the bar high and they encourage and motivate the teams they lead to deliver on the details. To lead a team, it’s important to appreciate the need to strive for excellence. Great leaders aren’t happy to accept the status quo, especially if the status quo can be improved upon.

Of course, these qualities can be learned, but many of us — thankfully — have them, innately. Few (if any) of us are happy with second best and, in a field driven by a desire to create delightful and memorable user experiences, we appreciate the importance of details and their place in the grand scheme of things. This is a great foundation on which to build leadership skills.

To thrive as a leader, it’s important to share this focus on the details with others, ensuring they understand and appreciate the role that the details play in the whole. Act as a beacon of excellence: lead by example; never settle for second best; give success and accept failure… and your team will follow you.

In Closing

As user experience design matures as a discipline, and the number of different specializations multiplies, so too does the discipline’s need for leaders, to nurture and grow teams. As a relatively new field of expertise, the opportunities to develop as a UX leader are tremendous.

Leadership is a skill and — like any skill — it can be learned. As I’ve noted throughout this series of articles, one of the best places to learn is to look to other disciplines for guidance, widening the frame of reference. When we consider leadership, this is certainly true.

There is a great deal we can learn from the world of business, and websites like Harvard Business Review (HBR), McKinsey Quarterly, and Fast Company — amongst many, many others — offer us a wealth of insight.

There’s never been a more exciting time to work in User Experience design. UX has the potential to impact on so many facets of life, and the world is crying out for leaders to step up and lead the charge. I’d encourage anyone eager to learn and to grow to undertake a skills audit, take the first step, and embark on the journey into leadership. Leadership is a privilege, rich with rewards, and is something I’d strongly encourage exploring.

Suggested Reading

There are many great publications, offline and online, that will help you on your adventure. I’ve included a few below to start you on your journey.

  • The Harvard Business Review website is an excellent starting point and its guide, HBR’s 10 Must Reads on Leadership, provides an excellent overview on developing leadership qualities.
  • Peter Drucker’s writing on leadership is also well worth reading. Drucker has written many books, one I would strongly recommend is Managing Oneself. It’s a short (but very powerful) read, and I read it at least two or three times a year.
  • If you’re serious about enhancing your leadership credentials, Michael D. Watkins’s The First 90 Days: Proven Strategies for Getting Up to Speed Faster and Smarter, provides a comprehensive insight into transitioning into leadership roles.
  • Finally, HBR’s website — mentioned above — is definitely worth bookmarking. Its business-focused flavor offers designers a different perspective on leadership, which is well worth becoming acquainted with.

This article is part of the UX design series sponsored by Adobe. Adobe XD is made for a fast and fluid UX design process, as it lets you go from idea to prototype faster. Design, prototype, and share — all in one app. You can check out more inspiring projects created with Adobe XD on Behance, and also sign up for the Adobe experience design newsletter to stay updated and informed on the latest trends and insights for UX/UI design.

Smashing Editorial
(ra, il)


Taken from:

Becoming A UX Leader

Hanapin’s PPC Experts Share How to Boost Your AdWords Quality Score with Landing Pages

It’s happened to the best of us. You return from lunch, pull up your AdWords account, and hover over a keyword only to realize you have a Quality Score of just three (ooof). You scan a few more keywords, and realize some others are sitting at fours, and you’ve even got a few sad twos.

Low Quality Scores like this are a huge red flag because they mean you’re likely paying through the nose for a given keyword without the guarantee of a great ad position. Moreover, you can’t necessarily bid your way into the top spot by increasing your budget.

You ultimately want to see healthy Quality Scores of around seven or above, because a good Quality Score can boost your Ad Rank, your resulting Search Impression Share, and will help your ads get served up more often.

To ensure your ads appear in top positions whenever relevant queries come up, today we’re sharing sage advice from PPC experts Jeff Baum and Diane Anselmo from Hanapin Marketing. During Marketing Optimization Week, they spoke to three things you can do with your landing pages today to increase your Quality Score, improve your Ad Rank, and pay less to advertise overall.

But first…

What is Quality Score (and why is it such a big deal?)

Direct from Google, Quality Score is an estimate of the quality of your ads, keywords, and landing pages. Higher quality ad experiences can lead to lower prices and better ad positions.

You may remember a time when Quality Score didn’t even exist, but it was introduced as a way for you to understand if you were serving up the best experiences possible. Upping your score per keyword (especially your most important ones) is important because it determines your Ad Rank in a major way:

Cost Per Click x Quality Score = Ad Rank

To achieve Quality Scores of seven and above you’ll need to consider three factors. We’re talkin’: relevancy, load time, and ease of navigation, which are consequently the very things Diane and Jeff say to focus on with your landing pages.

Below are the three actions Hanapin’s dynamic duo suggest you take to get the Ad Rank you deserve.

Where can you see AdWords Quality Score regularly?
If you’re not already keeping a close eye on this, simply navigate to Keywords and modify by adding the Quality Score column. Alternatively, you can hover over individual keywords to view case-by-case.

Tip 1) Convey the Exact Same Message From Ad to Landing Page

One of the perks of building custom landing pages fast, is the ability to carry through the exact same details from your ads to your landing pages. A consistent message between the two is key because it helps visitors recognize they’ve landed in the right place, and assures someone they’re on the right path to the outcome they searched for.

Here’s an ad to landing page combo Diane shared with us as an example:

Cool, 500 business cards for $8.50—got it. But when we click through to the landing page (which happens to be the brand’s homepage…)

  • The phone number from the ad doesn’t match the top of the page where we’ve landed.
  • The price in the ad headline doesn’t match the website’s headline exactly ($8.50 appears further down on the page, but could cause confusion).
  • While the ad’s CTA is to “order now”, the page we land on has tons to click on and offers up “Free Sample Kit” vs. an easy “Order Now’ option to match the ad. Someone may bounce quickly because of the amount of options presented.

As Jeff told us, the lesson here is that congruence builds trust. If you do everything to make sure your ads and landing pages are in sync, you’ll really benefit and likely see your Quality Score rise over time.

In a second example, we see strong message match play out really well for Vistaprint, wherein this is the ad:

And all of the ad’s details make it through to the subsequent landing page:

Improve your AdWords Quality Score with landing pages like Vistaprint's here.

In this case:

  • The price matches in the prominent sub-headline
  • The phone number matches the ad
  • Stocks, shapes and finishes are mentioned prominently on the landing page after they’re seen in the ad
  • The landing page conveys the steps involved in “getting started” (the CTA that appears most prominently).

Overall, the expectations are set up in the ad and fulfilled in the landing page, which is often a sign this advertiser is ideally paying less in the long run.

Remember: Google doesn’t tell you precisely what to fix.
As Jeff mentioned in Hanapin’s MOW talk, Google gives you a score, but doesn’t tell you exactly what to do to improve it. Luckily, we can help with reco’s around page speed, CTAs and more. Run your landing page through our Landing Page Analyzer to get solid recommendations for improving your landing pages.

Tip 2) Speed up your landing page’s load time

If you’re hit with a slow-loading page, you bounce quickly, and the same goes for prospects clicking through on your ads.

In fact, in an account Jeff was working on at Hanapin over the summer, in just one month they saw performance tank dramatically because of site speed. Noticing that most of the conversion drop off came from mobile, they quickly learned desktop visitors had a higher tolerance for slower load times, but they lost a ton of mobile prospects (from both form and phone) because of the lag.

Jeff recalls:

“we saw our ad click costs were going up, because our Quality Score was dropping due to the deficiency in site speed”.

Your landing page size (impacted by the images on your page) tends to slow load time, and—as we’ve seen with the Unbounce Landing Page Analyzer—82.2% of marketers have at least one image on their landing page that requires compression to speed things up.

As Jeff and Diane shared, you can check your page’s speed via Google’s free tool, Page Speed Insights and get their tips to improve. Furthermore, if you want to instantly get compressed versions of your images to swap out for a quick speed fix, you can also run your page through the Unbounce Landing Page Analyzer.

Pictured above: the downloadable images you can get via the Analyzer to improve your page speed and performance.

Tip 3) Ensure your landing page is easy to navigate

Using Diane’s analogy, you can think of a visit to your landing page like it’s a brick and mortar store. In other words, it’s the difference between arriving in a Nike store during Black Friday, and the same store any other time of the year. The former is a complete mess, and the latter is super organized.

Similarly, if your landing page experience is cluttered and visitors have to be patient to find what they’re looking for, you’ll see a higher bounce rate, which Google takes as a signal your landing page experience isn’t meeting needs.

Instead, you’ll want a clear information hierarchy. Meaning you cover need-to-know information quickly in a logical order, and your visitor can simply reach out and grab what they need as a next step. The difference is the visitor being able to get in and check out in a matter of minutes with what they wanted.

This seems easy, but as Diane says,

“Sometimes when thinking about designing sites, there’s so much we want people to do that we don’t realize that people need to be given information in steps. Do this first, then do that…”

As Jeff suggested, with landing pages, less can be more. So consider where you may need multiple landing pages for communicating different aspects of your offer or business. For example, if you own a bowling alley that contains a trampoline park and laser tag arena, you may want separate ads and landing pages for communicating the party packages for each versus cramming all the details on one page that doesn’t quite meet the needs of the person looking explicitly for a laser tag birthday party.

The better you signpost a clear path to conversion on your landing pages, the better chance you’ll have at a healthy Quality Score.

The job doesn’t really end

On a whole, Diane and Jeff help their clients at Hanapin achieve terrific Ad Rank by making their ad to landing pages combos as relevant as possible, optimizing load time, and ensuring content and options are well organized.

Quality Score is something you’ll need to monitor over time, and there’s no exact science to it. Google checks frequently, but it may be a few weeks until you see your landing page changes influence scores.

Despite no definitive date range, Diane encourages everyone to stay the course, and you will indeed see your Quality Score increase over time with these landing page fixes.

Original source:  

Hanapin’s PPC Experts Share How to Boost Your AdWords Quality Score with Landing Pages

Live Stream From Awwwards Berlin 2018: Showcasing Trends In UX Design

Designing the best experience is a challenge, and every designer and developer has their own way of tackling it. But, well, no matter how different our approaches are, one thing is for sure: We can learn a lot from each other.
To give you your dose of UX inspiration, we are happy to announce that our dear friends at Adobe, are streaming live from the Awwwards Conference which will take place in Berlin on February 8th and 9th.

View this article – 

Live Stream From Awwwards Berlin 2018: Showcasing Trends In UX Design