SkillAgentSearch skills...

Lineara.xyz

A tool for exploring the Linear A corpus

Install / Use

/learn @mwenge/Lineara.xyz
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

LinearA Explorer

The objective of the LinearA Explorer is to allow users to explore the complete Linear A corpus in a way that is intuitive and hopefully illuminating. But while it might be of passing interest to the curious visitor it must also be of practical use to interested Linear A scholars. That means that is must offer them something not currently available to them and allow them to investigate and explore the Linear A dataset in ways that were not previously open to them. While still a work in progress, I think the Linear A Explorer is on its way to achieving that.

Searhing and filtering

Introduction to Linear A

The LinearA Explorer is a visualization tool for exploring and researching the surviving documents of the Linear A language.

Linear A was the primary script used in palace and religious writings by the Minoan civilization in Crete from 1800 to 1450 BC. To this day it remains undeciphered though a lot of progress has been made in understanding the nature and purpose of the Linear A documents themselves. The Linear A Explorer is intended as a resource for making the published Linear A inscriptions as accessible as possible to those interested in understanding more about them.

Overview of the technologies used to build the Explorer

The extraction and construction of the images and Linear A corpus were performed using Python, imagemagick scripts and curl. The explorer itself consists simply of HTML, CSS, and Javascript. The explorer is custom-built, so no frameworks have been used in creating the visualization.

The data used for the explorer consists principally of the images of the Linear A documents and the transacriptions published by Louis Godart and Jean-Pierre Olivier in 1970 and of the tabulation and interpretation of the inscriptions by George Douros.

Sourcing and Extracting Data for the Explorer

Extracting and organizing the image files

The images of the Linear A tablets and their transcriptions are available in the three digitized volumes of Recueil des Inscriptions en Lineair A uploaded at https://cefael.efa.gr. The first step was to download the entire digitized volume using curl. I then created a series of imagemagick and python scripts that split each digitized page into separate files representing a single file per inscription and transcription. This gave me an image repository with a single cropped image file for each tablet. I then created a csv file that described the mapping of image files to each inscription and then developed a python script that used this to create an images folder for the app containing all the images of each tablet and inscription. Note that I'm using two repositories here. One at https://github.com/mwenge/LinearA for performing the data extraction and processing and another https://github.com/mwenge/LinearAExplorer/ for hosting the visualization app itself and the data we've extracted for analysis by the app.

Extracting and organizing the Linear A Text

The complete Linear A corpus is relatively small, consisting of only a few hundred documents. The total corpus would fill only 5 or 6 pages if all the documents were placed end to end. A Linear A scholar George Douros has produced a spreadsheet that tabulates each document and helpfully provides word breaks, ideograms, and numerals broken out for each line of each document. When converted to csv format this spreadsheet allows me to create the core of the app, which is a structured representation of each document in JSON format. I do this by writing a python script that takes in the csv file and writes a javascript array of JSON objects to a javascript file called LinearAInscriptions.js.. Here is a snippet of the file, showing how the document HT1 is represented in as a JSON object:

["HT1",{
    "image": "images/HT1-Inscription.jpg",
    "name": "HT1",
    "parsedInscription": "𐘿𐘽𐘉𐄁\n𐘸𐘁𐄙𐄘𐄍\n𐙀𐘲𐄖\n𐘆𐘆𐘍𐘥𐄔𐄈\n𐙂𐘰𐘯𐄙𐄏\n𐘇𐘴𐘅𐘙𐄙𐄋",
    "tracingImage": "images/HT1-Tracing.jpg",
    "transcription": "𐘿𐘽𐘉𐄁𐘸𐘁𐄙𐄘\n𐄍𐙀𐘲𐄖𐘆𐘆\n𐘍𐘥𐄔𐄈𐙂𐘰𐘯𐄙\n𐄏𐘇𐘴𐘅𐘙𐄙𐄋",
    "translatedWords": [
        "QE-RA2-U",
        "𐄁",
        "\n",
        "KI-RO(owed)",
        "197",
        "\n",
        "𐙀-SU",
        "70",
        "\n",
        "DI-DI-ZA-KE",
        "52",
        "\n",
        "KU𐘰-NU",
        "109",
        "\n",
        "A-RA-NA-RE",
        "105"
    ],
    "transliteratedWords": [
        "QE-RA2-U",
        "𐄁",
        "\n",
        "KI-RO",
        "197",
        "\n",
        "𐙀-SU",
        "70",
        "\n",
        "DI-DI-ZA-KE",
        "52",
        "\n",
        "KU𐘰-NU",
        "109",
        "\n",
        "A-RA-NA-RE",
        "105"
    ],
    "words": [
        "𐘿𐘽𐘉",
        "𐄁",
        "\n",
        "𐘸𐘁",
        "𐄙𐄘𐄍",
        "\n",
        "𐙀𐘲",
        "𐄖",
        "\n",
        "𐘆𐘆𐘍𐘥",
        "𐄔𐄈",
        "\n",
        "𐙂𐘰𐘯",
        "𐄙𐄏",
        "\n",
        "𐘇𐘴𐘅𐘙",
        "𐄙𐄋"
    ]
}],

This encapsulates all the information about the inscription that we need to visualize it in the app. The image files, the raw transacription, and arrays representing the parsed words of the inscription both in Linear A, transliterated Linear A syllabograms, and proposed translations where applicable.

Loading the data into the visualization app

The relatively small dataset we're working with means that we can load the entire dataset into the browser when the user visits the site. This is done with a simple function that iterates the entire Map of JSON objects and creates a floating div element for each inscription. We let the browser do the work of laying out the elements in the way that best fits the page:


function loadExplorer() {
  for (var inscription of inscriptions.values()) {
    loadInscription(inscription);
  }
}

var wordsInCorpus = new Map();
function loadInscription(inscription) {
  var item = document.createElement("div");
  item.className = 'item-container';
  item.id = inscription.name;
  item.setAttribute("onclick", "showCommentaryForInscription('" + inscription.name + "')");

  addImageToItem(item, inscription.image, inscription.name)
  addImageToItem(item, inscription.tracingImage, inscription.name)

  var transcript = document.createElement("div");
  transcript.className = 'item';
  transcript.setAttribute("inscription", inscription.name);
  for (var i = 0; i < inscription.words.length; i++) {
    var word = inscription.words[i];
    var elementName = word == "\n" ? "br" : "span";
    var span = document.createElement(elementName);
    if (elementName == "span") {
      span.textContent = word;

      var searchTerm = word.replace(/𐝫/g, "");
      if (wordsInCorpus.has(searchTerm)) {
        wordsInCorpus.set(searchTerm, wordsInCorpus.get(searchTerm) + 1);
      } else {
        wordsInCorpus.set(searchTerm, 1);
      }
      span.id = inscription.name + "-transcription-" + i;
      span.setAttribute("onmouseover", "highlightWords(event, '" + inscription.name + "', '" + i + "')");
      span.setAttribute("onmouseout", "clearHighlight(event, '" + inscription.name + "', '" + i + "')");
      span.setAttribute("onclick", "updateSearchTerms(event, '" + inscription.name + "', '" + i + "')");
    }
    transcript.appendChild(span);
  }
  item.appendChild(transcript);

  transcript = document.createElement("div");
  transcript.className = 'item';
  transcript.setAttribute("inscription", inscription.name);
  for (var i = 0; i < inscription.translatedWords.length; i++) {
    var word = inscription.translatedWords[i];
    var elementName = word == "\n" ? "br" : "span";
    var span = document.createElement(elementName);
    if (elementName == "span") {
      span.textContent = word + " ";
      span.id = inscription.name + "-translation-" + i;
      span.setAttribute("onmouseover", "highlightWords(event, '" + inscription.name + "', '" + i + "')");
      span.setAttribute("onmouseout", "clearHighlight(event, '" + inscription.name + "', '" + i + "')");
      span.setAttribute("onclick", "updateSearchTerms(event, '" + inscription.name + "', '" + i + "')");
    }
    transcript.appendChild(span);
  }
  item.appendChild(transcript);

  var label = document.createElement("div");
  label.className = 'label';
  label.textContent = inscription.name;
  item.appendChild(label);
  inscription.element = item;

  container.appendChild(item);
}

The result is seen here:

Explorer after loading

Hovering over text, searching and filtering

The simplicity of our data model means that implementing searching and filtering is easy.


function applySearchTerms() {
  var searchTerms = document.getElementById("search-terms");
  clearHighlights();
  for (var inscription of inscriptions.values()) {
    var shouldDisplay = true;
    for (
View on GitHub
GitHub Stars29
CategoryDevelopment
Updated1mo ago
Forks5

Languages

HTML

Security Score

80/100

Audited on Mar 7, 2026

No findings