Admin Panel

Image Search

Purpose of these web components is to display semi-random images in editorial content that users can collect by clicking on them to collect points.

Prerequisites

You need to generate an access token for the user in your backend and then you need to set this access token to all SCILL components using the setAttribute function available for all HTMLElement objects.

An example code and more info on that can be found in our web integration guide.

Config URL

You need to provide a config URL that returns a JSON document with this structure:

Config-URL Format
{
  "images": [
    "__IMAGE_1_URL__",
    "__IMAGE_2_URL__"
  ]
}

There must be at least the same amount of images available as the challenge goal is setup in Admin Panel (see below).

Randomly displaying images

You need to add this tag somewhere in your page and provide a selector for the content piece in which the image will be displayed as an overlay. Typically this is some longer text like an article. Provide the selector in the container attribute.

For example #article would search for a DOM element like <div id="article">...</div> in your page. Or providing a selector like .my-article-class would find this DOM element <div class="my-article-class">...</div>. This web component uses document.querySelector to query the first matching in your page.

Adding image display widget
<scill-image-search
        class="scill-web-component"
        app-id="639938110737711114"
        challenge-id="639964880380887041"
        driver-challenge-id="639951911993638926"
        event-challenge-id="640001118993973251"
        config-url="https://www.example.com/image-search-config.json"
        minimum-scroll-depth="200" 
        display-delay="1" 
        display-delay-variation="0" 
        max-image-width="350px" 
        container="#article"
        min-vertical-offset="0"
        max-vertical-offset="0"
        random-value="4"
        random-stretch="1.0"
        first-image-always="true"
        notification-image-url="http://www.example.com/image.png">
</scill-image-search>

Discussion

This web components requires one challenge that holds the images found for the user per day and another challenge to show the number of images found for the whole lifetime of the event. Both challenges are triggered whenever a user clicks on the image to collect it and will reset every day at midnight.

Another challenge is used for counting page impressions and to place images on the website at regular intervals.

You can import a template for this application in your own developer account.

Image display formula

For each page impression a random value is calculated based on the random-value and random-stretch settings. The code to calculate if an image should be displayed looks like this (internally):

Internal code to calculate if image should be shown
const imageIndexToBeShown = challengeInfo.challenge.user_challenge_current_score;
const maxRandomValue = parseInt(this.randomValue, 10) + (imageIndexToBeShown * parseFloat(this.randomStretch));
const randomValue = Math.round(Math.random() * (maxRandomValue));
console.log('SCILL: Random value: ', randomValue, maxRandomValue);

if (randomValue !== 1) {
    return null;
}

// Display image

So, in essence, the more images the user has already found, the greater is the pool of random values that we choose 1 as our target value. You can define how much this “stretching” is by defining a float value with the random-stretch attribute.

This table demonstrates various settings and how many page impressions in average must be generated for the next image to be displayed and the number of total page impressions a user has to do in total to find all images.

Random Value Random Stretch PIs for 1st image PIs for 2nd image PIs for 3rd image PIs for 4th image PIs required
2 1.0 2 3 4 5 14
2 2.0 2 4 6 8 20
4 0.0 4 4 4 4 16
4 1.0 4 5 6 7 22
4 2.0 4 6 8 10 28

These are the basic “slots” at which images will be displayed. However, to bring in some randomness, a pseudo random value based on the user-id attribute (which is set by JavaScript when setting the access token) the curent day of the week, day of the month and current hour is created. This value spreads between -2 and +2 and will shift the image slots around.

It’s not a random value, but is deterministic to allow validation on server side. See anti cheat below for more infos on that topic.

Attributes

This web component accepts these attributes to customize image display and how often the image is displayed.

app-id string REQUIRED

This is the app id for your application.

challenge-id string REQUIRED

Provide the unique id of your challenge that counts the number of images found per day.

driver-challenge-id string REQUIRED

The challenge id of the page impressions counter. Create a challenge with a high goal. It will be reset after an image is found to start counting up page impressions until the next image is displayed.

event-challenge-id string OPTIONAL

This is a challenge holding total images found for the whole lifetime of the event. It will be used to show the number of images found in total in notifications. It’s typically the same challenge-id that you set in the overall progress indicator (see below).

config-url string REQUIRED

The URL to a config file (see above) that configures the images which are used in the image search component.

minimum-scroll-depth string OPTIONAL

The minimum vertical scroll position of the page that the user must have scrolled down that the image is displayed. Use a value greater than 0 if you want that your users scroll down the page. The image will be allways positioned relative to the current vertical scroll position (i.e. the user will not need to scroll up again to see the image)

display-delay string OPTIONAL

The delay in seconds that the user must be on the page until the image is displayed. Use that to make image display a bit more prominent as it pops up after the web page is loaded.

display-delay-variation string OPTIONAL

The number of seconds that the display-delay is randomized. Formula is delay = random(display-delay-variation*2) - display-delay-variation. If display-delay is 6 and display-delay-variation is 3 then the image appears between 3 and 9 seconds.

max-image-width string OPTIONAL

Provide the value of the maximum width of the image overlaying your content as a CSS value (i.e. 100% or 300px). A good value is 350 as this is the minimum width of most mobile screen resolutions. Default is 350px.

container string OPTIONAL

Provide a CSS-selector (it’s passed to document.querySelector) in which the image will be put. If this is not provided then the image will be put inside the scill-image-search tag. Important: The container needs to have position: relative set as the image is positionied absolute.

min-vertical-offset integer OPTIONAL

Use this to set the minimum vertical offset (CSS: top) that the image is positioned relative to the container. It’s positioned is randomized between the min-vertical-offset and the max-vertical-offset (see below). Default is 0.

max-vertical-offset integer OPTIONAL

Use this to set the maximum vertical offset (CSS: top) that the image is positioned relative to the container. Only set the value! It’s positioned is randomized between the min-vertical-offset and the max-vertical-offset (see below). Default is 0.

random-value integer OPTIONAL

The average number of page impressions required to reveal an image. Default is 4.

random-stretch float OPTIONAL

The more images a user has been found, the harder it should be to find the next image. This float defines how much stretching is being applied. If the value is 0.0 then no stretching is applied, if the value is 1.0 then it’s very moderate. See the table above for some example values. Default is 1.0.

first-image-always boolean OPTIONAL

If you set this to true then the first image will always be displayed as soon as the user opens the first web page. However, this only applies if the user has not found any image yet and the page impression counter is below 2 as we don’t want to nag the user too much in the beginning.

notification-image-url string OPTIONAL

Image to be put in the notification popup. Should be an image highlighting the event or a brand.

Displaying status

Users should have a place to see how many images they have already found and if they have completed the daily task.

Add this web component to your webpage to show the current status to the user:

Adding status page
<scill-image-search-status
        class="scill-web-component"
        app-id="639938110737711114" 
        challenge-id="639964880380887041"          
        title="Daily Challenge achieved"
        config-url="http://www.example.com/image-search-config.json">
</scill-image-search-status>

Discussion

This web component displays a nice status of daily found images.

The image search status component

The image search status component

It is synced in real-time (as always in SCILL) and will immediately update the status once an image is found.

Attributes

This web component accepts these attributes to customize image display and how often the image is displayed. Attributes like app-id and challenge-id must be the same as in the <scill-image-search> to link the status to the image search widget.

app-id string REQUIRED

This is the app id for your application.

challenge-id string REQUIRED

Provide the unique id of your challenge that counts the number of images found

config-url string REQUIRED

The URL to a config file (see above) that configures the images which are used in the image search component.

config-url string REQUIRED

The URL to a config file (see above) that configures the images which are used in the image search component.

Displaying overall process

If you want to display a progress bar showing the daily or overall process, then you can use the progress web component which only displays a customizable progress bar for a specific challenge.

Adding status page
<scill-challenge-progress
        class="scill-web-component"
        app-id="639938110737711114" 
        challenge-id="640001118993973251"
        height="30" 
        border-radius="10"
        fill-background="#31E914" 
        background="#333333"
        title="Current progress"
        completed-title="Challenged completed">
</scill-challenge-progress>

Attributes

app-id string REQUIRED

This is the app id for your application.

challenge-id string REQUIRED

Provide the unique id of your challenge that counts the number of images found over the lifetime of the event.

height integer OPTIONAL

Sets the height (in pixels) of the progress bar. Default is 20.

border-radius integer OPTIONAL

The border-radius (in pixels) used to render the progress bar. Default is 0.

fill-background string OPTIONAL

The color of the progress bar. Default is red.

background string OPTIONAL

The color of the progress bar background. Default is black.

title string OPTIONAL

Set a title. If set, the current counter of the challenge like 23/100 is added after the title.

completed-title string OPTIONAL

Set a title that is used once the challenge is completed.

Displaying a leaderboard

A leaderboard can be added to the landing page of the event to give users an indication where they are located compared to the community.

Adding status page
<scill-leaderboard
        class="scill-web-component"
        app-id="639938110737711114" 
        leaderboard-id="640000955260207115">
</scill-leaderboard>

Discussion

This leaderboard is fully working with pagination and is updated in real-time whenever something changes in the board. It can be fully customized using CSS.

More info on the leaderboard component can be found in the Web Component Reference Guide: Leaderboard Web Component.

Attributes

You need to provide the App-Id and leaderboard id and that’s it. As there are so many ways to customize a table, this component needs to be styled with CSS (instead of setting attributes). The default style is light and heavily inspired by material design.

app-id string REQUIRED

This is the app id for your application.

leaderboard-id string REQUIRED

The id of the leaderboard.

Anti Cheat

Image collect is a client side action and therefore could be easily hacked. To prevent this, server side validation is required.

Remember the overall process of this system:

  1. Each page impressions generates an event that is sent server side. Cheaters will not be able to send those events, because they don’t have the API key to do that.
  2. The image search widget reads the current users page impression value and has a list of “image slots” that are determined in general by the random-value and random-stretch.
  3. A pseudo random value is calculated based on the user id and some time and date values. This value spreads between -2 and +2 and shifts those image slots around, so that there is some randomness for the user.
  4. If the user clicks on the found image, an event should be sent. Hackers could now replicate those events to collect all images via script. To prevent that, we must validate that on server side.
  5. Whenever the user clicks an image, on server side we do the same calculation that is done of client side to figure out if the image is displayed. And if server side calculates, that in fact the user should now see an image the image collect event is sent.
  6. As we have some randomness in it, this randomness must be deterministic so that client and server side come to the same conclusion by having the same input. For this, generating that pseudo random “shift” value must be the same on client and server side. Check out the code on the right side to replicate that code in your backend.
Creating deterministic pseudu random value (TypeScript)
calculateDetermisticRandomValue(userId: string): number {
    // Calculate a number based on the current day of week and day of month
    const date = new Date();
    const id = parseInt(userId, 10) + date.getDay() + date.getDate() + date.getHours();
    // Take the last number
    let letter = parseInt(id.toString().substr(-1, 1), 10);
    // Number is between 0 and 9, now move to -5 and 4
    letter -= 5;
    // Scale number to be between -2 and 2
    letter *= 0.4;
    return Math.round(letter);
}