Timer question / help needed

40 views
Skip to first unread message

cathy jun

unread,
Oct 22, 2025, 12:48:44 PM10/22/25
to LIONESS Lab help and discussion
Hello,

I'm following up on a similar question to this thread (https://groups.google.com/d/msgid/lioness-lab/2186f6bc-6978-40ce-824c-454ac7096279n%40googlegroups.com). I am also trying to program an experiment where participants are presented with letter-grids and asked to identify how many times a specific letter appears in each grid. Each letter-grid occupies its own stage in Lioness. I want to allow participants 2 minutes to complete as many letter-search grids as possible. Half of my participants will count the letter Ws and the other half will count the letter Ms. After 2 minutes, participants will auto-advance to the next part of the experiment. 

  1. Can you help me program a 2-minute timer that covers multiple stages in LIONESS and then auto-advances participants to the next part of the experiment when time is up? 
  2. In the second part of the experiment, participants who counted Ws will be paired with another participant who counted Ms to exchange messages. 
I have began to write some of the javascript (shared below) but there are 2 main issues that I cannot seem to fix: the 2-minute timer restarts whenever participants move to the next letter-search grid (next stage) and it doesn't auto advance to the next part of the experiment once the timer is out and it instead refreshes the page it is on. I have also included the HTML for the timer in one of the elements.

I have tried keeping all of the possible grids in one stage and just using the built in auto advance function in Lioness. The issue with that approach is that I have 30 possible grids that participants can do and the page lags to the point where the built in timer would not start until the entire page loads which takes more than 2 minutes. Because of the lag, the built in auto advance doesn't work and doesn't take participants to the next part of the experiment. If it's easier to help me fix that issue than write the code, I'm open to going down that approach. But at the moment, neither approach is working.

I can also make my lab public if that's easier for someone to help me. Thank you!

// ------------------- Vars ---------------------
var stagePrefix   = 'task1grid';         // make this unique per grid page if you want per-page start/end/duration
var roundPrefix   = 'round';              // shared across the whole round
var TIME_LIMIT_SEC = 120;                  // 2 minutes for the round
var TIMER_EL_ID    = 'round-timer';       // <div id="round-timer">02:00</div>
// ----------------------------------------------

function nowMs(){ return Date.now(); }
function toIso(ms){ return new Date(ms).toISOString(); }
function pad2(n){ return (n<10?'0':'') + n; }
function fmtMMSS(sec){ sec=Math.max(0,Math.floor(sec)); var m=Math.floor(sec/60), s=sec%60; return pad2(m)+':'+pad2(s); }
function read(k){ try { return (typeof getValue==='function') ? getValue(k) : null; } catch(e){ return null; } }
function write(k,v){ record(k, v); } // Lioness-native

// Round-level vars (shared)
var roundStartVar  = roundPrefix + '_globalStart';   // ms since epoch (string)
var roundLimitVar  = roundPrefix + '_timeLimitSec';  // "120"
var roundLeftVar   = roundPrefix + '_timeLeftSec';   // remaining (secs)
var roundTimeUpVar = roundPrefix + '_timeUp';        // "1" if ended

// Per-page vars (for page-level timing)
var startVar    = stagePrefix + '_start';
var endVar      = stagePrefix + '_end';
var durationVar = stagePrefix + '_duration';
var answerCountVar = stagePrefix + '_answerCount';

// Figure out which numeric input is present on THIS page
// You showed two inputs gated by getValue('gridtype') == '1' or '2'
var pageAnswerVar = (read('gridtype') === '2') ? 'rd1_grid2answer' : 'rd1_grid1answer';

// -------- Init on page load --------
(function initPage(){
  // Start the round clock on the first grid page
  if (read(roundStartVar) == null) {
    write(roundStartVar, String(nowMs()));
    write(roundLimitVar, String(TIME_LIMIT_SEC));
    write(roundTimeUpVar, "0");
  }

  // Per-page timing (optional but useful)
  if (read(startVar) == null) write(startVar, toIso(nowMs()));

  // Start / resume countdown display + hard timeout
  startOrResumeCountdown();

  // Hook manual submission so we capture the final value even if they click Continue before timeout
  hookManualContinue();
})();

// -------- Countdown --------
var countdownHandle = null;

function startOrResumeCountdown(){
  var startMs = Number(read(roundStartVar) || 0);
  var limitSec = Number(read(roundLimitVar) || TIME_LIMIT_SEC);
  var timeUp = String(read(roundTimeUpVar) || "0");

  if (!isFinite(startMs) || startMs <= 0) {
    startMs = nowMs(); write(roundStartVar, String(startMs));
  }

  // Already expired? finalize and move on
  if (timeUp === "1") { finalizePage(read(pageAnswerVar) || "0", /*autoAdvance=*/true); return; }

  // Draw now + tick each second
  updateCountdown(startMs, limitSec);
  if (countdownHandle) clearInterval(countdownHandle);
  countdownHandle = setInterval(function(){ updateCountdown(startMs, limitSec); }, 1000);
}

function updateCountdown(startMs, limitSec){
  var elapsedSec = (nowMs() - startMs) / 1000;
  var leftSec = Math.max(0, Math.ceil(limitSec - elapsedSec));
  write(roundLeftVar, String(leftSec));
  var el = document.getElementById(TIMER_EL_ID);
  if (el) el.textContent = fmtMMSS(leftSec);

  if (leftSec <= 0) {
    clearInterval(countdownHandle);
    write(roundTimeUpVar, "1");
    finalizePage(read(pageAnswerVar) || "0", /*autoAdvance=*/true);
  }
}

// -------- Finalize writes (shared by manual + timeout) --------
function finalizePage(finalCountValue, autoAdvance){
  var endMs = nowMs();
  write(endVar, toIso(endMs));

  // duration from this page's start
  var startIso = read(startVar);
  try {
    var sMs = Date.parse(startIso);
    if (isFinite(sMs)) {
      var durSec = Math.max(0, Math.round((endMs - sMs)/1000));
      write(durationVar, String(durSec));
    }
  } catch(e){}

  // store final numeric answer (optional mirror + guaranteed write)
  var n = parseInt(finalCountValue, 10);
  if (isNaN(n)) n = 0;
  write(answerCountVar, String(n));

  if (autoAdvance) tryClickNextOrSubmit();
}

// -------- Manual continue hook (no double-submit) --------
function hookManualContinue(){
  // If page uses a form, capture submit:
  var form = (document.forms && document.forms[0]) || null;
  if (form) {
    form.addEventListener('submit', function(){
      finalizePage(read(pageAnswerVar) || "0", /*autoAdvance=*/false);
    });
  }
  // Also try a common Continue button (in case it isn't a form submit)
  var btn = document.querySelector('button[type=submit], input[type=submit], .NextButton, [data-continue]');
  if (btn) {
    btn.addEventListener('click', function(){
      finalizePage(read(pageAnswerVar) || "0", /*autoAdvance=*/false);
    });
  }
}

// -------- Auto-advance helper (timeout path only) --------
function tryClickNextOrSubmit(){
  var btn = document.querySelector('button[type=submit], input[type=submit], .NextButton, [data-continue], [name=next]');
  if (btn) { btn.click(); return; }
  var form = (document.forms && document.forms[0]) || null;
  if (form && typeof form.submit === 'function') form.submit();
}

in...@lioness-lab.org

unread,
Oct 22, 2025, 2:20:44 PM10/22/25
to cathy jun, LIONESS Lab help and discussion

Hi Cathy,

 

Thanks for your question. I think you can make your life much easier if this is all done inside the same stage. I created a quick (and ugly) prototype that seems to do what you want. You can find it in the repository under “grids_LM”.

 

The key is to save participant responses in an array, and upon timeout you can write that as a comma-separated string to the database.

 

Hope this helps.

 

Cheers, Lucas

--
You received this message because you are subscribed to the Google Groups "LIONESS Lab help and discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lioness-lab...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/lioness-lab/94862812-f89e-4252-a026-066b2a98cacfn%40googlegroups.com.

Cathy Jun

unread,
Oct 22, 2025, 3:31:28 PM10/22/25
to LIONESS Lab help and discussion
Hi Lucas, 

Thank you so much for getting back to me and helping me, Lucas! I went over your example but I created a copy of part of the instrument that I put together and made it public (BJS Grid Example - Copy of v. 14). 

I would also like to have all of the grids in the same stage but the built in timeout would not work and auto advance because I suspect that there is a lag with all of the images loading. The grids that I have are images. Could you please help me get stage 6 to work and auto advance to stage 8 without lags? 

The issue is that I have 30 possible grids that participants can do and the page lags to the point where the built in timer would not start until the entire page loads which takes more than 2 minutes. Because of the lag, the built in auto advance doesn't work and doesn't take participants to the next part of the experiment. If you could help me fix that issue, that'll be awesome. 

Thank you so much again!! I really appreciate it. 

Best,
Cathy

in...@lioness-lab.org

unread,
Oct 22, 2025, 3:56:50 PM10/22/25
to Cathy Jun, LIONESS Lab help and discussion

Hi  Cathy,

 

You have getValue('gridtype') == '1' inside the “display conditions” field. These fields are checked very frequently (like every 10ms or 100ms or so), which means that the server is bombed with getValue requests. Can you find a workaround so that the display conditions fields have entries that do not rely on server requests? That way I think your problem should be solved.

 

Cheers, Lucas

Cathy Jun

unread,
Oct 25, 2025, 11:15:25 AM10/25/25
to LIONESS Lab help and discussion
Thank you for all your help Lucas! I'm running the experiment soon so I really appreciate you getting back to me quickly. I was able to get the grid stage together but I was hoping to get help on another area. 

I've made a copy of my experiment public since I think it may be easier for you to understand where the question is coming from (BJS Message Exchange Help 23 - Copy of v. 22 - Lucas). 

In stage 12, I'm trying to have participants write two different types of messages based on their assignment. In stage 13, each participant should receive their partner's respective message from stage 12. The messages aren't pulling into stage 13. I'm guessing the issue can be that their partner did not write the message before participants arrive in stage 13 since I cannot control how fast participants go through stage 12. I changed the continue button to "wait for others" before advancing to stage 13 but I still can't get the messages to properly pull. 

Would you be able to help me pull the messages please? 

Thank you again!

Best,
Cathy

Cathy Jun

unread,
Oct 25, 2025, 4:39:15 PM10/25/25
to LIONESS Lab help and discussion
Hi Lucas, 

I was able to resolve the issue. Thank you so much for your help!! Appreciate it.

Best,
Cathy

Reply all
Reply to author
Forward
0 new messages