Learn Measure Blog Live About
Betty Crocker Cake Mix—Coffee Cinnamon.

Wake Lock API case study: 300% increase in purchase intent indicators on BettyCrocker.com

Nothing is worse when cooking with a mobile device than its screen turning off in the middle of a recipe step. Learn how cooking site BettyCrocker.com used the Wake Lock API to prevent this from happening.

Updated

For nearly a century, Betty Crocker has been America's source for modern cooking instruction and trusted recipe development. Launched in 1997, their site BettyCrocker.com today receives more than 12 million visitors per month. After they implemented the Wake Lock API, their indicators of purchase intent were about 300% higher for wake lock users compared to all users.

The retired iOS and Android apps

Released to much fanfare in 2014, Betty Crocker recently took their native apps out of the Apple App Store and the Google Play Store after they had been deprioritized. For a long time, the Betty Crocker team has preferred adding new features to the mobile site instead of the native apps. The technical platform the native apps were created on was outdated, and the business did not have the resources to support updating and maintaining the apps moving forward. The web app also was objectively way bigger traffic-wise, more modern, and easier to enhance.

The native apps did have one killer feature, though, that their users loved:

Millennial cooking pro tip: the @BettyCrocker mobile app doesn't dim or lock when you're following a recipe. —@AvaBeilke

80% of people cook with a device in the kitchen, but screen dimming and locking is a problem. What did @BettyCrocker do? Updated their app to NOT dim when users are in a recipe. —@Katie_Tweedy_

Bringing the killer feature to the web with the Wake Lock API

When cooking with a device, there is nothing more frustrating than having to touch the screen with messy hands or even your nose when the screen turns off. Betty Crocker asked themselves how they could port the killer feature of their native app over to the web app. This is when they learned about Project Fugu and the Wake Lock API.

A person kneading dough on a kitchen table covered in flour

The Wake Lock API provides a way to prevent the device from dimming or locking the screen. This capability enables new experiences that, until now, required a native app. The Wake Lock API reduces the need for hacky and potentially power-hungry workarounds.

Requesting a wake lock

To request a wake lock, you need to call the navigator.wakeLock.request() method that returns a WakeLockSentinel object. You will use this object as a sentinel value. The browser can refuse the request for various reasons (for example, because the battery is too low), so it is a good practice to wrap the call in a try…catch statement.

Releasing a wake lock

You also need a way to release a wake lock, which is achieved by calling the release() method of the WakeLockSentinel object. If you want to automatically release the wake lock after a certain period of time has passed, you can use window.setTimeout() to call release(), as shown in the example below.

// The wake lock sentinel.
let wakeLock = null;

// Function that attempts to request a wake lock.
const requestWakeLock = async () => {
try {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
console.log('Wake Lock was released');
});
console.log('Wake Lock is active');
} catch (err) {
console.error(`${err.name}, ${err.message}`);
}
};

// Request a wake lock…
await requestWakeLock();
// …and release it again after 5s.
window.setTimeout(() => {
wakeLock.release();
wakeLock = null;
}, 5000);

The implementation

With the new web app, users should be enabled to easily navigate through a recipe, complete steps, and even walk away without the screen locking. To achieve this goal, the team first built a quick front-end prototype as a proof of concept and to gather UX input.

After the prototype proved useful, they designed a Vue.js component that could be shared across all their brands (BettyCrocker, Pillsbury, and Tablespoon). Even though only Betty Crocker had a native app, the three sites do have a shared code base, so they were able to implement the component once, and deploy it everywhere, as shown in the screenshots below.

BettyCrocker.com wake lock toggle
BettyCrocker.com wake lock toggle.
Pillsbury.com wake lock toggle
Pillsbury.com wake lock toggle.
Tablespoon.com wake lock toggle
Tablespoon.com wake lock toggle.

When developing the component based on the new site's modernized framework, there was a strong focus on the ViewModel layer of the MVVM pattern. The team also programmed with interoperability in mind to enable functionality on legacy and new frameworks of the site.

To keep track of viewability and usability, Betty Crocker integrated analytics tracking for core events in the wake lock lifecycle. The team utilized feature management to deploy the wake lock component to a single site for initial production rollout, and then deployed the feature to the rest of the sites after monitoring usage and page health. They continue to monitor analytics data based on the usage of this component.

As a failsafe for users, the team created a forced timeout to disable the wake lock after one hour of inactivity. The final implementation they settled on was in the short-term a toggle switch on all recipe pages across their sites. In the long-term, they envision a revamped recipe page view.

The wake lock container

var wakeLockControl = () => {
return import(/* webpackChunkName: 'wakeLock' */ './wakeLock');
};

export default {
components: {
wakeLockControl: wakeLockControl,
},
data() {
return {
config: {},
wakeLockComponent: '',
};
},
methods: {
init: function(config) {
this.config = config || {};
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
this.wakeLockComponent = 'wakeLockControl';
} else {
console.log('Browser not supported');
}
},
},
};

The wake lock component

<template>
<div class="wakeLock">
<div class="textAbove"></div>
<label class="switch" :aria-label="settingsInternal.textAbove">
<input type="checkbox" @change="onChange()" v-model="isChecked">
<span class="slider round"></span>
</label>
</div>
</template>

<script type="text/javascript">
import debounce from 'lodash.debounce';

const scrollDebounceMs = 1000;

export default {
props: {
settings: { type: Object },
},
data() {
return {
settingsInternal: this.settings || {},
isChecked: false,
wakeLock: null,
timerId: 0,
};
},
created() {
this.$_raiseAnalyticsEvent('Wake Lock Toggle Available');
},
methods: {
onChange: function() {
if (this.isChecked) {
this.$_requestWakeLock();
} else {
this.$_releaseWakeLock();
}
},
$_requestWakeLock: async function() {
try {
this.wakeLock = await navigator.wakeLock.request('screen');
//Start new timer
this.$_handleAbortTimer();
//Only add event listeners after wake lock is successfully enabled
document.addEventListener(
'visibilitychange',
this.$_handleVisibilityChange,
);
window.addEventListener(
'scroll',
debounce(this.$_handleAbortTimer, scrollDebounceMs),
);
this.$_raiseAnalyticsEvent('Wake Lock Toggle Enabled');
} catch (e) {
this.isChecked = false;
}
},
$_releaseWakeLock: function() {
try {
this.wakeLock.release();
this.wakeLock = null;
//Clear timer
this.$_handleAbortTimer();
//Clean up event listeners
document.removeEventListener(
'visibilitychange',
this.$_handleVisibilityChange,
);
window.removeEventListener(
'scroll',
debounce(this.$_handleAbortTimer, scrollDebounceMs),
);
} catch (e) {
console.log(`Wake Lock Release Error: ${e.name}, ${e.message}`);
}
},
$_handleAbortTimer: function() {
//If there is an existing timer then clear it and set to zero
if (this.timerId !== 0) {
clearTimeout(this.timerId);
this.timerId = 0;
}
//Start new timer; Will be triggered from toggle enabled or scroll event
if (this.isChecked) {
this.timerId = setTimeout(
this.$_releaseWakeLock,
this.settingsInternal.timeoutDurationMs,
);
}
},
$_handleVisibilityChange: function() {
//Handle navigating away from page/tab
if (this.isChecked) {
this.$_releaseWakeLock();
this.isChecked = false;
}
},
$_raiseAnalyticsEvent: function(eventType) {
let eventParams = {
EventType: eventType,
Position: window.location.pathname || '',
};
Analytics.raiseEvent(eventParams);
},
},
};
</script>

Results

The Vue.js component has been deployed on all three sites and delivered great results. During the period from December 10th, 2019 to January 10th, 2020, BettyCrocker.com reported the following metrics:

  • Of all Betty Crocker users with a browser compatible with the Wake Lock API, 3.5% of them enabled the feature immediately, making it a top-5 action.
  • The session duration for users who enabled the wake lock was 3.1× longer than for users who did not.
  • The bounce rate for users who enabled the wake lock was 50% lower than for those not using the wake lock feature.
  • Indicators of purchase intent were about 300% higher for wake lock users compared to all users.

3.1×

Longer session duration

50%

Lower bounce rate

300%

Higher purchase intent indicators

Conclusions

Betty Crocker has seen fantastic results using the Wake Lock API. You can test the feature yourself by searching for your favorite recipe on any of their sites (BettyCrocker, Pillsbury, or Tablespoon) and enabling the Prevent your screen from going dark while you cook toggle.

Use cases for wake locks do not stop at recipe sites. Other examples are boarding pass or ticket apps that need to keep the screen on until the barcode has been scanned, kiosk-style apps that keep the screen on continuously, or web-based presentation apps that prevent the screen from sleeping during a presentation.

We have compiled everything you need to know about the Wake Lock API in a comprehensive article on this very site. Happy reading, and happy cooking!

Acknowledgements

The person kneading dough photo courtesy of Julian Hochgesang on Unsplash.

Last updated: Improve article