PALS3004/G308 Web Programming
SPEECH, HEARING & PHONETIC SCIENCES
UCL Division of Psychology and Language Sciences
PALS3004/G308 Web programming for research in psychology and language sciences

7. Scripting Web Experiments

Learning Objectives

By the end of the session you should:

Topics

  1. Three part structure
  2. Broadly speaking we can divide a typical web experiment into three phases: Welcome, Testing and Conclusion. Each phase could be implemented as a separate web page, although if you want to communicate data between the phases, the phases can be implemented as three different "views" of the same web page. See demo7-1.html for how this approach might be implemented.

    Welcome

    The Welcome phase is where you tell the subject about the experiment, possibly giving some example trials. It can also be used to collect personal details about the subject, for example, their name, sex and age. You can also use the Welcome phase to obtain the consent of the subject for the use of their details/responses for research purposes.

    You would typically supply a button or link to start the test proper.

    Testing

    The Testing phase will consist of the presentation of a sequence of stimuli and the recording of the subject responses. Typically you will let the subject know how many stimuli the subject has seen and how many there are to go.

    Each stimulus will make some change to the web page. It is important that the presentation of a new stimulus should be readily noticeable by the subject. The response by the subject could be collected from a button press, a key press, some entered text or the adjustment of a slider.

    In some tests it is necessary to measure the reaction time of the subject. In this case the timing of the appearance of the stimulus must not be predictable and the Javascript performance timer can be used to record the time of stimulus presentation and the time of response to calculate the reaction time interval, see Section 4.

    When the response has been collected (or if the subject has not responded within a given time) then the program should display the next stimulus. If the test session is long, it may be advisable to incorporate explicit breaks in the test. Once all stimuli have been presented, the application should automatically move to the Conclusion phase.

    Conclusion

    The Conclusion phase is useful for thanking the subject for taking part. Subjects are always keen to see how well they have done, so this may also be an opportunity to provide some summary of each subject's performance. You might even show the subject's performance compared to the average of all subjects who have taken the test.

    Behind the scenes, it may be in this phase that you upload the test results to the server. This aspect will be covered in Week 8.

  3. Test Data Management
  4. Your web experiment will probably need to maintain the following data:

    1. Subject identification data
    2. Total number of stimuli
    3. Number of stimuli presented so far
    4. Table of stimuli to present (typically in random order)
    5. Table of response to stimuli
    Create the stimulus table

    The stimulus table should record the different test conditions and identify the different stimuli. For example, assume there are two conditions (1,2) and each can be realised as one of two different stimuli (A,B). If you wanted a test in which each combination was presented 10 times, you might create a stimulus table with:

    var stimtab=[];
    for (var i=0;i<10;i++) stimtab.push({ condition:1, stimulus:"A" });
    for (var i=0;i<10;i++) stimtab.push({ condition:1, stimulus:"B" });
    for (var i=0;i<10;i++) stimtab.push({ condition:2, stimulus:"A" });
    for (var i=0;i<10;i++) stimtab.push({ condition:2, stimulus:"B" });
    var stimcnt=stimtab.length;
    

    Thus the stimulus table is an explicit list of the different stimuli together with a record of the underlying test conditions.

    Shuffle the stimulus table

    Commonly it is easiest to build the stimulus table in some understandable order, but then randomise the order of presentation using some "shuffling" of elements. Here is a function that can randomise the order of a table using the Fisher-Yates Shuffle algorithm.

    /**
     * Randomize array element order in-place.
     * Using Fisher-Yates shuffle algorithm.
     */
    function shuffleArray(array) {
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return array;
    }
    

    For example:

    shuffleArray(stimtab);
    
    Keep a record of current stimulus and save response

    In an experiment you will need functions to start a new test, to show one stimulus and to record one response. The test start function may be called by a button press event on the welcome page, the response record function may be called by the subject choosing a response. When a response has been received, the function may then display the next stimulus, or if all stimuli have been presented, go to the conclusion phase.

    Let's look at some example code for this:

    var stimtab=...      // your stimulus table
    var stimcnt=...      // number of stimuli
    var stimpos;         // current stimulus
    function displayStimulus() {
       var curstim=stimtab[stimpos];
       // ... code for displaying stimulus on screen
    }
    function startNewTest() {
       stimpos=0;
       displayStimulus();
    }
    function recordResponse(rtype) {
       stimtab[stimpos].response=rtype;
       stimpos++;
       if (stimpos==stimcnt)
          // ... code for going to Conclusion
       else
          displayStimulus();
    }
    

    To get the response buttons to send different codes to recordResponse(), you could use HTML like this:

    <button onclick="recordResponse(1)">One</button>
    <button onclick="recordResponse(2)">Two</button>
    <button onclick="recordResponse(3)">Three</button>
    

  5. Dynamic loading
  6. Your stimuli may need access to resources stored on the server, for example each stimulus may be an image. To display a series of different images on a web page, you will need to use Javascript to change the src attribute of an image element. For example:

    stimtab.push({ condition:1, stimulus:"car.jpg" });
    stimtab.push({ condition:1, stimulus:"van.jpg" });
    ... // etc
    function displayStimulus() {
       var curstim=stimtab[stimpos];
       document.getElementById("img1").src=curstim.stimulus;
    }
    ... // in HTML
    <img id="img1" src="blank.jpg">
    

    A disadvantage of the above is that the images are loaded from the server when the displayStimulus() function is called. This may take an indeterminate amount of time. This may make the test run rather slowly, and will not be any good if you want to measure reaction times.

    A solution is to pre-load the images when the test starts. The idea is to read the images into your script, and in doing so load them from the server into the client browser. Then when the browser needs the images later in the test, it can access them from the local cache rather than from the server. The code might look like this:

    var imglist=[ "car.jpg", "van.jpg", "bus.jpg" ];
    function preload() {
       for (var i=0;i<imglist.length;i++) {
          var img=new Image();
          img.src=imglist[i];
       }
    }
    ...
    <body onload="preload()">
    

  7. Date & Time
  8. These objects and methods allow you to find the date and time and to measure duration.

    Date object & methods

    The Date object holds a date and time with millisecond precision. When you create a new Date object with the "new" operator it is automatically initialised to the current date and time.

    var now=new Date();
    alert("The time now is " + now.toTimeString());
    

    Note that the Date object doesn't update, it just stores the time at which it was created. If you want to build a clock, you will need to create a series of Date objects.

    The following methods allow you to access the individual elements of the date, or to return strings representing the date and/or time in some standard formats.

    MethodDescription
    getDate()Get the day as a number (1-31)
    getDay()Get the weekday as a number (0-6)
    getFullYear()Get the four digit year (yyyy)
    getHours()Get the hour (0-23)
    getMilliseconds()Get the milliseconds (0-999)
    getMinutes()Get the minutes (0-59)
    getMonth()Get the month (0-11)
    getSeconds()Get the seconds (0-59)
    getTime()Get the time (milliseconds since January 1, 1970)
    toDateString()Converts the date portion of a Date object into a readable string
    toTimeString()Converts the time portion of a Date object to a string
    toISOString()Returns the date as a string, using the ISO standard
    toLocaleString()Converts a Date object to a string, using locale conventions
    toLocaleDateString()Returns the date portion of a Date object as a string, using locale conventions
    toLocaleTimeString()Returns the time portion of a Date object as a string, using locale conventions

    The performance timer

    Although the Date objects hold the time to millisecond precision, the objects may not be updated with millisecond accuracy. So the difference in milliseconds between two Date objects may not be particularly accurate.

    Fortunately most browsers also implement a "performance" timer, which is typically used by developers to improve the efficiency of their code. The performance timer has sub-millisecond accuracy, and differences in times between calls can give valid intervals as low as one microsecond.

    The performance timer is a global object that can be called without initialisation. The method performance.now() returns a floating-point number in milliseconds. However the absolute value of this number does not mean anything - it may just be the number of milliseconds since the browser started. However you can use differences in its value to measure times:

    var stime=performance.now();
    ... do something that takes a long time
    var etime=performance.now();
    alert("that thing took " + (etime-stime).toFixed(3) + "ms");
    
    Delayed and timed execution

    Sometimes you may want something to happen after a given period of time, or maybe you want something to happen every so often. You can ask the browser to call a function in your program after a given length of time with the setTimeout function, or you can ask the browser to call a function in your program regularly with the setInterval function.

    To request that a function is called after N milliseconds, use setTimeout(funcname,N), for example:

    function hideElement() {
       document.getElementById("hider").style.display="none";
    }
    // hide the element in 1s
    setTimeout(hideElement,1000);
    

    To request that a function is called every N milliseconds, use var timer=setInterval(funcname,N). To cancel the timer you can then use clearInterval(timer). For example:

    function updateClock() {
       var now=new Date();
       document.getElementById("clock").innerHTML=now.toTimeString();
    }
    // update the clock every second
    setInterval(updateClock,1000);
    

    Generally the setTimeout function is easier to use than setInterval, because you don't have to worry about cancelling the timer when your script finishes. The last example could have been written:

    function updateClock() {
       var now=new Date();
       document.getElementById("clock").innerHTML=now.toTimeString();
       // update the clock every second
       setTimeout(updateClock,1000);
    }
    updateClock();
    

  9. Look and feel
  10. The demonstration pages we have developed so far have very little styling. To improve the look and feel of your web application you need to include a stylesheet. You can either develop your own style sheet, or find a "template" on the web that you can download and customise.

    Skeleton style

    The course web pages are constructed using (an old version of) the Skeleton template. This template consists of two style sheets "normalize.css" and "skeleton.css" and uses a web font called "Raleway". Your HTML head section should include the lines:

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" 
     href="//fonts.googleapis.com/css?family=Raleway:400,300,600" 
     type="text/css">
    <link rel="stylesheet" href="css/normalize.css" type="text/css">
    <link rel="stylesheet" href="css/skeleton.css" type="text/css">
    

    assuming the styles sheets have been unpacked into a folder called "css" in the current folder.

    Skeleton also comes with a sample HTML file which you can use as the basis for your own pages.

    Skeleton is designed to be responsive to different web-browsing platforms, allowing the page to be re-formatted for phones as opposed to tablets as opposed to desktops for example. It does this by dividing sections into columns which may stack side-by-side or on top of one another depending on space.

    The whole container should be in a div with a class of "container". Each set of columns should be in a div with a class of "row". Each row can be divided into columns using a div with a class of "num columns", where num is a width expressed as number between one and sixteen, and which expresses the column width as a fraction of 16. For a quick start and one column, just put this at the top of the body section:

    <body>
    <div class="container">
    <div class="row">
    <div class="sixteen columns">
    

    and this at the end:

    </div>
    </div>
    </div>
    </body>
    
    Full screen operation

    Most browsers now support full-screen operation - so that users can try out your experiment without being distracted by the toolbars and menus of the browser.

    A Javascript library called ScreenFull is a simple and portable way to build a full-screen application.

    You download screenfull.js and include it in your program with:

    <script src="screenfull.js"></script>
    

    To change to full screen, you need to call screenfull.request(); to check whether full screen operation is supported you can check the property screenfull.enabled. You can only make requests to go full screen after a user request, such as a button click hander, e.g.:

    function button1click() {
       if (screenfull.enabled) screenfull.request();
    }
    ...
    <button onclick="button1click()">Full Screen</button>
    

    Try this:

Examples of on-line experiments

Exercises

  1. Implement "Page Switching" demonstration (demo7-1.html). Modify the code (ex7-1.html) to increase the number of presentations to 20 and to provide a fourth "take a break" page every 5 presentations.
  2. Implement "Image Labelling" demonstration (demo7-2.html). Modify the code (ex7-2.html) to incorporate your own set of images and to pre-load the images in an initialisation phase.
  3. Implement "Reaction Time" demonstration (demo7-3.html). Modify the code (ex7-3.html) to add a "welcome" page and a "conclusion" page.
  4. Take one of the preceding demonstrations and add a style sheet to improve the layout (ex7-4.html). You can either create your own stylesheet or download a template from the web.

Homework

  1. Complete the exercises you did not finish in class. Upload your answers to your area on the course website.
  2. Narrow down the design of your web experiment. Find some relevant research articles that describe the scientific basis for your experiment.
  3. Investigate what stimuli are used in the topic area of your experiment and collect resources you will need, for example any word lists, sound recordings or pictures.
  4. Make decisions about the conditions you will use, and what data you will need to extract from each response.
  5. Finalise the Experiment Planning Guide for your proposed web experiment.
  6. Make a start on your coursework web site - create a page template and write some place-holder pages that follow the project assessment guidelines.

Word count: . Last modified: 16:47 17-Nov-2016.