Log every link click

Twine Version: 2.3.16
Story Format: sugarcube 2.36.1

I would like to log all link clicks, and all button clicks ( to record the players path (click + time) through the game)
Is there a way I could do that by adding some js functionality, or will I just have to do it in every link/button

thanks very much for any help


1 Like

Within a <<link>> you can use <<set>>, it allows you yo create either a specific variable each time, or an array that will keep each choice. However, if you’re allowing players to use the back arrow you’ll loose some information here.
Also, there’s a time() function (https://www.motoslave.net/sugarcube/2/docs/#functions-function-time) that should help for the time thing (another array, or nested arrays). Again the use of the back arrow might prove annoying.

If you add this to your JavaScript section:

/* Log Clicks - Start */
$(document).on(":passageend", function () {
	$("#passages a").on("click", function (ev) {
		if (!State.variables.clickLog) {
			State.variables.clickLog = [];
		State.variables.clickLog.push({ passage: State.peek(1).title, type: "link", label: $(this).text(), time: new Date(Date.now()) })
	$("#passages button").on("click", function (ev) {
		if (!State.variables.clickLog) {
			State.variables.clickLog = [];
		State.variables.clickLog.push({ passage: passage(), type: "button", label: $(this).text(), time: new Date(Date.now()) })
Save.onSave.add(function(save) {  // Requires SugarCube v2.36.0 or later
	if (State.variables.clickLog) {
		save.state.history[save.state.index].variables.clickLog = State.variables.clickLog;
/* Log Clicks - End */

then that will automatically log any links or buttons clicked within the passages (it won’t include UI bar clicks) to the $clickLog variable as an array of objects. Each object in the array has .passage, .type, .label, and .time properties you can use to figure out what was clicked, where, and when. For example, $clickLog[0].label would tell you the label of the first item clicked. The .type property will either be “link” or “button”, so you will be able to tell what was clicked if you have a link and a button with the same label in a passage.

Note: The data on the $clickLog variable may get quite large, so I wouldn’t recommend using this in a game where they may go through hundreds of link and button clicks, since all of that data the history may begin slowing down the saves, loads, and passage transitions.

Hope that helps! :slight_smile:

1 Like

That is exactly what I was looking for, and it teaches me something about events like :passageend and how they might be useful to me.

Thank you very much

1 Like

I created a small prototype, and the problem is repeatable by creating a new MVC 2 Web app project. When the link is clicked, a JavaScript LinkClicked() function sends the link details to a PHP script clicker counter for logging. The log is a CSV file. The … At Log The User, Date, And Time For Every Link Click ? mark the check box to enable the Link Log* . Click* the Update button. The Link Log can be viewed any … Clicking a different link counts as a click for each link clicked . … lead out of the search results page can log clicks or impressions in Search Console.

How to track every passage transition in Sugarcube

(this is online tracking so subject to GDPR btw)

Google Analytics

var lockID = LoadScreen.lock();
importScripts('https://www.googletagmanager.com/gtag/js?id=XXX').then(function () {
  window.dataLayer = window.dataLayer || [];
  window.gtag = function () {
  gtag('js', new Date());
  gtag('config', 'YOUR_ID');
  $(document).on('click', 'a[data-passage]', function () {
    gtag('event', 'Navigation', {
      event_label : $(this).attr('data-passage'),
      event_category : 'GuestClick'
  if (State.size > 0) { Engine.show(); }
}).catch(function (error) {
  console.warn('Could not load "gtag" library.');

Matomo Analytics

 var _paq = window._paq = window._paq || [];
 (function() {
   _paq.push(['setTrackerUrl', u+'matomo.php']);
   _paq.push(['setSiteId', 'SITE ID']);
   var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
   g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
 $(document).on('click', 'a[data-passage]', function (ev) {
   var passage = $(this).attr('data-passage');
   if ( passage && _paq ) { 
      _paq.push(['trackPageView', passage])