Pet Shop Business Card


Pet Shop Business Card


In this video, we’ll be taking a look at a business card AR experience we created for a fictional pet shop using the Studio tool.

For further context on the project, please check out this video where we discuss the idea behind the experience.

Studio Overview

ZapWorks Studio is our most powerful and versatile augmented, virtual, and mixed reality toolkit. It supports 3D models, visual timeline tools for animation, 360 imagery & video, and additional flexibility through scripting for those who wish to use it.

Studio really has loads of features for you to take advantage of, but it’s also easy to get started using our documentation and learning resources.

Project Download

Click here to download the assets, including the zpp project.

For more information on importing a ZPP project in Studio, see our documentation article on Importing and Exporting.

Experience Overview

The experience starts with the individual digital assets overlaid as they appear on the physical tracking image. By overlaying the individual elements of the card, we have the ability to animate them in Studio.

An opening transition plays where the interactive buttons are highlighted, then they are re-positioned and a 3D model of a cat appears. A 2D animated hand appears above the cat indicating that it can be tapped on to trigger fur color changes and ‘meows’.

The buttons then become active as indicated by their own tap icon, one of them dialing a telephone number, another linking to a website, and the third linking to a Google Maps location.


We start in the main root symbol, with 3 subsymbols defined within the Symbol Definitions panel.

Subsymbols are reusable components that can be used to define functionality that is to be used more than once.

For more information on subsymbols, you can check out the Subsymbols section.

lookFor Subsymbol

If we double-click on a symbol within the Symbol Definitions panel we’ll be taken into that symbol. Each symbol is like a self-contained project with its own Media Library, Hierarchy and Controllers. We can tell which symbol we’re currently in by looking at the breadcrumb trail just above the 3D preview.

In the Controllers panel, we can switch the main controller’s state to active and play the ‘loop’ timeline, to see the symbol in action. Its purpose is to instruct the user to point their device at the tracking image, if it cannot be seen in the device’s camera view. This symbol is very useful when the content of the experience is heavily tied to the tracking image, and we want to encourage the user to keep it in view.

The symbol uses a placeholder tracking image that is overridden from the parent symbol. It does this using references, a way of exposing a property of a node so it can be set from outside the symbol. In this case, the ‘materials’ property of the ‘cover’ plane is referenced.

Returning back to the parent symbol using the breadcrumb bar and selecting the ‘lookFor’ node in the Hierarchy, we can see the ‘target_image’ reference at the top of the properties panel. It’s set to a smaller version of the target image, created specifically for this purpose.

3D Model Subsymbol

The 'catModel’ is a 3D model subsymbol of a cat, along with its animations.

When importing your own 3D models into Studio, you will notice they appear as subsymbols in the bottom right of your screen, below the Media Library. Clicking into ‘catModel’, we can see the model in the 3D view and a set of timeline animations within the Controllers panel.

The ‘full’ timeline contains all of the 3D model’s animations in one sequence, whereas the ‘jump’ and ‘lookAround’ timelines are singular animations that we’ve extracted for use in the experience. These timelines are used within the ‘jump’ and ‘lookaround’ timelines of the parent symbol.

For more information on how the timelines within a subsymbol can be controlled by another symbol, make sure to check out our documentation page on Playing 3D Animations.

hand Subsymbol

The ‘hand’ subsymbol is where the animated hand that appears above the cat model is situated.

The ‘anim’ timeline within the main controller is made up of a series of separate images being switched on and off, creating the 2D animation.

The hand images have been placed within a billboard group, which means they will always face the user regardless of the angle they’re viewing the experience from.

Media Library

The Media Library contains all of our assets including our button images, cat textures, sounds and more.

Hierarchy Overview

The Hierarchy is made up of a number of items, first we have a ‘code’ script node. This houses the majority of the interactivity for the experience including:

  • The logic for changing the cat’s texture.
  • Showing and hiding content depending on whether the target image is seen or not.
  • Controlling animations.

Target and Content Groups

Beneath the code script, we have the target image group and a content group within that. The content group is placed within the target image group because we want the content tracked to the target image.

Within the content group, there are a number of further organized sub-groups, as well as a square mask to hide parts of the scene after the intro animation has finished. Masking is a technique that hides parts of a scene as if they were behind an invisible object.

pointerdown Events

The ‘buttons’ group contains pointerdown scripts for when the user taps on the images.

‘Pointerdown’ scripts are really useful in Studio, allowing you to create actions such as going to websites or triggering animations when a user presses a button.

Within the ‘pointerdown’ scripts here, you can find the code responsible for taking the user to a website, Google Maps location, and dialing a telephone number.

// Launching a website
parent.on("pointerdown", (e) => {

//Dialing a phone number
parent.on("pointerdown", (e) => {
    Z.device.launchUrl("tel:+0123456789", false);

3D Hotspot

The ‘cat’ group is where we place the ‘catModel’ and ‘hand’ subsymbols, the sound effect for the cat meowing, and a plane acting as a ‘hotspot’ for detecting when the user taps on the cat. The term hotspot here refers to a point of interactivity.

The reason we need to use a separate ‘non-visible’ plane positioned in front of the cat model is that 3D models do not support touch events. For more information on touch events, see Z.Object.

Controllers, States & Timelines

The Controllers panel contains two primary controllers, ‘main’ and ‘catType’.

The ‘main’ controller contains the intro timeline to be played when the user first scans the target image. It also includes the ‘jump’ timeline where the cat jumps into the scene as well as the ‘lookAround’ timeline, which is a seamless loop of the cat's idle animation.

The ‘catType’ controller contains six different states, one for each of the different colored fur textures. These states are used within the ‘code’ script to change the color of the cat’s fur when tapped.

States are a simpler alternative to timelines and best used in situations where a more complex animation is not required.

The other controller of note is the ‘fingerPrompt’ controller which contains an ‘on’ timeline that plays the ‘hand’ subsymbol’s animation.

This timeline also contains a ‘loop’ timeline label which is used in the ‘code’ script to loop the animation from the point after it’s faded in.

More information on Timeline Labels.

Main Script Node

We can now take a closer look at how the various states and timelines are activated in the main script node 'code'. We won’t be covering every line of code but we’ve provided comments within the script for you to reference on your own.

seen and notseen Events

The ‘seen’ and ‘notseen’ functions are run when the target enters and leaves the device’s camera view.

In the ‘seen’ function, we’re playing the ‘intro’ timeline and deactivating the ‘lookFor’ subsymbol.

Inversely in the ‘notseen’ function we’re stopping the ‘intro’ animation and activating the ‘lookFor’ subsymbol.

We’re also stopping the ‘jump’ and ‘lookaround’ timelines in case either of them is being played when the target image is lost from the device’s camera.

//play the animation timeline when the target is seen and deactivate the look for prompt
target.on("seen", seen);
function seen() {;

//pause the animation timeline when the target is not seen and activate the look for prompt
target.on("notseen", notseen);
function notseen() {

Timeline complete Events

The ‘jump’ timeline is told to play after the ‘intro’ timeline has completed. And when the ‘jump’ timeline has completed, the ‘lookAround’ timeline is told to play.

The ‘fingerPrompt’ subsymbol is also instructed to play after the ‘jump’ timeline has finished playing, but only if the cat has not yet been tapped on.

//change the cat's jump animation when the intro animation cycle is complete
symbol.controllers.main.elements.intro.on("complete", () => {;

//play the cat's lookAround animation when the jump animation is complete and display the fingerPrompt if that cat has not been tapped on thus far
symbol.controllers.main.elements.jump.on("complete", () => {
    if (catTapped == false) {;
    else {;

Changing a 3D Models Texture

When the pointerdown event is fired from the hotspot, the ‘fingerPrompt’ is deactivated and the ‘changeCat’ function is called.

Within the ‘changeCat’ function, the active ‘catType’ state is changed, modifying the texture applied to the cat model. The ‘meow’ sound effect is also played.

//change the cat's skin texture when the user presses the invisible hotspot plane above the cat and set the catTapped variable, used above, to true
symbol.nodes.hotspot.on("pointerdown", (e) => {
    catTapped = true;

//change the cat's fur texture when it's tapped on, called from the pointerdown event handler function above
function changeCat() {
    cat += 1;
    if (cat >= cats.length) {
        cat = 0;
    symbol.nodes.meow.restart(); //trigger the cat's sound effect

Timeline Label Loop

Finally the ‘fingerPrompt’s’ ‘on’ timeline is restarted when it completes. Instead of restarting from the beginning however, it restarts from the ‘loop’ timeline label, meaning only a section of the timeline is looped.

//loop the hand prompt from the timeline label position when the animation completes
symbol.controllers.fingerPrompt.elements.on.on("complete", () => {;

If we preview the project we can see that we’ve managed to stay under the recommended 5MB download size so the project will unlock quickly once scanned.

Designer and Widgets Projects

Use the links below to be taken to the Designer and Widgets project videos, as well as the asset preparation video:

Open in new window