WARNING: Triplelog Writer is now Jatijm. Visit jatijm.com for more information.


Visit jatijm.com for an updated version of Triplelog Writer. Jatijm is not currently compatible with most components and there will not be any future updates to Triplelog Writer. However much of syntax below will eventually be incorporated into Jatijm.


If you don't need any customization you can simply visit https://triplelog.com/writer to get started. For your own version you can download a zip file11 Less than 200 KB zipped. of the project. You can either open the index.html file in a browser or spin up your favorite server and visit that page.

Get Started


Triplelog Writer includes a markdown renderer so just write some markdown in the textarea. Then click the Preview button in the top left to see the rendered content. When you wish to make edits, click the ✏️ in the top left and add more text. The Marked renderer is used and supports most CommonMark and Github Flavored syntax.

Download HTML

When your content is complete, click the Save/Load button. Enter a file name and then click the Download HTML button in the popup. An HTML file with that file name22 Or that name with (1), etc. appended if the name is not unique will be downloaded to your normal Downloads folder.


To save your progress, click the Save/Load button. You have two options to save: download a zip file or save to browser. The text and any data for charts and tables will be included with either option.

If you save to browser, you can set the name of your project and it will be saved to your browser's localStorage. To load the file from localStorage you will need to use the same browser and the same URL. Each browser has a quota for this storage that is 5-10 MB. Download zip files and delete old files from the browser to save space.

If you download a zip file, then you can upload the zip later to any browser or send to someone else. You can also upload a .md or plain .txt file. The zip file includes the generated HTML file.

Table of Contents

In the top right of your screen, a + button toggles the table of contents. This outline is automatically generated by headings tags.

In editing mode, this outline allows you to skip to the desired place in your writing. In preview mode or in the final HTML, this outline skips to the desired section of the page.


To add components to your text, click the desired button in the left dashboard and then follow the relevant instructions.

Sidenotes and Endnotes

Adding a sidenote33 A sidenote looks like this. is as simple as including the text inside of {{#sidenote}} and {{/sidenote}}. They will be numbered automatically and appear in the right margin.

Add an endnote1 by including the text inside of {{#endnote}} and {{/endnote}}. They will be numbered automatically and appear at the end of the article. Links to the endnote and then back to the original location are included.


Add equations like x32\frac{x^3}{2} by adding LaTeX\LaTeX syntax inside of {{#latex}} and {{/latex}}. For display mode, use replace the word "latex" with "display" so that text like {{#display}}\frac{x^3}{2}.{{/display}} will become

The syntax is rendered by KaTeX\KaTeX so anything they support should be possible. Everything is rendered to HTML so that you only need to include the KaTeX\KaTeX CSS file and not the JS file.


Code that is inside of a fenced code block will be highlighted by PrismJS. Start a fenced code block on a new line with ~~~ followed by the language. Then include your lines of code and finish with a line that is ~~~. Alternatively, you can use 3 tick marks (```) instead of 3 tildes.

var x = 0;

The above code will become:

var x = 0;

The code will be syntax highlighted to produce HTML so that you only need to include the Prism CSS file.

For inline code, include the code inside of 1 tick mark so `var x = 0;` becomes var x = 0;. This code is not highlighted, however.

INFO: To make tabs work inside of the textarea, select the Add Tab checkbox in the settings popup.

Modals and Tooltips

A modal is a popup that takes up most of the screen. Include the This is a modal. text inside of {{#modal.link}} and {{/modal.link}} followed immediately by the content inside of {{#modal.content}} and {{/modal.content}}.

You should be able to include markdown and most components inside of a modal link and/or its content. 22\frac{\sqrt{2}}{2} links are not underlined but they are possible. A small image in a sidenote44 can become large when clicked.

A tooltip is a smaller popup that appears Right here. to the linked text. Just include the link text inside of {{#tooltip.link}} and {{/tooltip.link}} followed immediately by the content inside of {{#tooltip.content}} and {{/tooltip.content}}.

Like modals, it should be possible to include most components inside of the tooltip links and/or content.

This content should slide down as you scroll.
WARNING: On mobile devices, readers will have to scroll right to see side containers and they will not slide down.

Side Container

A side container is a bit like a sidenote but it follows a section of text as the reader scrolls down. If there is a graphic or piece of information that is important for the reader to have at hand while they are reading a few paragraphs, then side containers help make that possible.

Create a side container by wrapping the side content inside of {{#sidecontainer.content}} and {{/sidecontainer.content}}. Then include several paragraphs of text, charts, or whatever. Then finally end the container by adding {{sidecontainer.end}}.

The side container can have charts, tooltips, LaTeX\LaTeX , and pretty much anything other than things like sidenotes.

WARNING: Your side container should not be very tall. Clashes with future paragraphs will occur if the side is taller than the main content.

Full Container

This content should slide down as you scroll.
SUCCESS: This box is temporarily pinned to the top.

A full container takes the same width as a normal paragraph but when it hits the top of the screen it stays there until a given amount of content scrolls past.

Create a full container by wrapping the content inside of {{#fullcontainer.content}} and {{/fullcontainer.content}}. Then include several paragraphs of text, charts, or whatever. Then finally end the container by adding {{fullcontainer.end}}.

The full container can have charts, tooltips, LaTeX\LaTeX , and pretty much anything other than things like sidenotes.

WARNING: Your full container should not be very tall. Tall containers will leave very little room for other content.

Full Width

A full width component takes up more of the screen's width than a normal paragraph, but it does not slide.

var x = 0;

Details and Carousels

The details component is a wrapper for the details/summary HTML tag that collapses content.

How to Add Click the button
Where to Add Anywhere

Add a carousel to let the reader choose from several images or whatever. The following code snippets come from the Hello World Collection.

// Hello World in C++ (pre-ISO)

#include <iostream.h>

main() { cout << "Hello World!" << endl; return 0; }
// Hello world in JavaScript

console.log("Hello World");

# Hello world in Python 3 (aka Python 3000)

print("Hello World")
# Hello World in R
cat("Hello world\n")
# Hello world in Julia

println("Hello, World!")
-- Hello World in Haskell
main = putStrLn "Hello World"
% Hello World in MATLAB.

disp('Hello World');
# Hello World in Lua

print "Hello world"

  // Hello world in PHP
  echo 'Hello World!';
// Hello world in TypeScript

alert('Hello world!');
% Hello World! in LaTeX
Hello World!
// Hello World in Java

class HelloWorld {
  static public void main( String args[] ) {
    System.out.println( "Hello World!" );
<!-- Hello World in HTML -->
<TITLE>Hello World!</TITLE>
Hello World!

Carousels can be added to sidenotes55
MidwestNortheastSouthWestLocation1910 U.S. Population by Region
MidwestNortheastSouthWestLocation1920 U.S. Population by Region
MidwestNortheastSouthWestLocation1930 U.S. Population by Region
MidwestNortheastSouthWestLocation1940 U.S. Population by Region
MidwestNortheastSouthWestLocation1950 U.S. Population by Region
MidwestNortheastSouthWestLocation1960 U.S. Population by Region
MidwestNortheastSouthWestLocation1970 U.S. Population by Region
MidwestNortheastSouthWestLocation1980 U.S. Population by Region
MidwestNortheastSouthWestLocation1990 U.S. Population by Region
MidwestNortheastSouthWestLocation2000 U.S. Population by Region
MidwestNortheastSouthWestLocation2010 U.S. Population by Region
MidwestNortheastSouthWestLocation2020 U.S. Population by Region
, side containers, modals, and more. The content within carousels can be just about anything.


Charts rely on vega-lite so you should be able to create just about any chart. You will need to learn the proper syntax. Check out their example charts.

Click the chart button, upload a csv or json data file, and set the options to get your chart. The $schema will automatically be version 5 if you don't set it. The data will automatically be added from the csv or json. You can also leave the data field blank and manually enter or copy/paste an array of json objects into the data object in the options.

You set a width and/or height of the chart in pixels. The chart will automatically be constrained to a max width of 100% of its container and 75% of the screen's height. Generally it is possible to make charts that look acceptable in mobile and desktop sites, but they are not perfectly responsive at the moment. Setting the width/height of a chart to "container" to make it responsive will probably not work.

By default, the charts should look fine without including the vega-lite js files but they will not be interactive.


Charts can be included anywhere, including
192019401960198020002020Year050,000,000100,000,000150,000,000200,000,000250,000,000300,000,000350,000,000PopulationWestSouthNortheastMidwestLocationU.S. Population By Decade


You can use Github Flavored markdown table syntax for small tables. Or you can upload a csv and create a large table that will automatically be paginated. If you create a table by uploading a csv, then what appears in the editor will look like the below code:

| Location | 2010 Population | 2020 Population | 2010-2020 Growth |
| --- | --: | --: | --: |
| United States | 308745538 | 331449281 | 7.35% |
| South | 114555744 | 126266107 | 10.22% |
| West | 71945553 | 78588572 | 9.23% |
| Midwest | 66927001 | 68985454 | 3.08% |
| Northeast | 55317240 | 57609148 | 4.14% |
| California | 37253956 | 39538223 | 6.13% |
| Texas | 25145561 | 29145505 | 15.91% |
| Florida | 18801310 | 21538187 | 14.56% |
| New York | 19378102 | 20201249 | 4.25% |
| Pennsylvania | 12702379 | 13002700 | 2.36% |

The table.info block includes a unique id for the table data followed by a header row and then the markdown formatting row. Then there is a table.preview block with the first page of data. Changing this preview data will have no impact on the final table. All data is loaded from the original csv. Do not change the id of the table or the data will no longer be connected.

You can change the header row. Shorten, lengthen, capitalize, or completely change the column names to whatever you wish. You can also change the alignment of each column by changing the formatting row as per the Github flavored specs. Left alignment is :-- (or the default ---), right alignment is --:, and center alignment is :-:.

Location 2010 Population 2020 Population 2010-2020 Growth
United States 308745538 331449281 7.35%
South 114555744 126266107 10.22%
West 71945553 78588572 9.23%
Midwest 66927001 68985454 3.08%
Northeast 55317240 57609148 4.14%
California 37253956 39538223 6.13%
Texas 25145561 29145505 15.91%
Florida 18801310 21538187 14.56%
New York 19378102 20201249 4.25%
Pennsylvania 12702379 13002700 2.36%
Illinois 12830632 12812508 -0.14%
Ohio 11536504 11799448 2.28%
Georgia 9687653 10711908 10.57%
North Carolina 9535483 10439388 9.48%
Michigan 9883640 10077331 1.96%
New Jersey 8791894 9288994 5.65%
Virginia 8001024 8631393 7.88%
Washington 6724540 7705281 14.58%
Arizona 6392017 7151502 11.88%
Massachusetts 6547629 7029917 7.37%
Tennessee 6346105 6910840 8.90%
Indiana 6483802 6785528 4.65%
Maryland 5773552 6177224 6.99%
Missouri 5988927 6154913 2.77%
Wisconsin 5686986 5893718 3.64%
Colorado 5029196 5773714 14.80%
Minnesota 5303925 5706494 7.59%
South Carolina 4625364 5118425 10.66%
Alabama 4779736 5024279 5.12%
Louisiana 4533372 4657757 2.74%
Kentucky 4339367 4505836 3.84%
Oregon 3831074 4237256 10.60%
Oklahoma 3751351 3959353 5.54%
Connecticut 3574097 3605944 0.89%
Puerto Rico 3725789 3285874 -11.81%
Utah 2763885 3271616 18.37%
Iowa 3046355 3190369 4.73%
Nevada 2700551 3104614 14.96%
Arkansas 2915918 3011524 3.28%
Mississippi 2967297 2961279 -0.20%
Kansas 2853118 2937880 2.97%
New Mexico 2059179 2117522 2.83%
Nebraska 1826341 1961504 7.40%
Idaho 1567582 1839106 17.32%
West Virginia 1852994 1793716 -3.20%
Hawaii 1360301 1455271 6.98%
New Hampshire 1316470 1377529 4.64%
Maine 1328361 1362359 2.56%
Rhode Island 1052567 1097379 4.26%
Montana 989415 1084225 9.58%
Delaware 897934 989948 10.25%
South Dakota 814180 886667 8.90%
North Dakota 672591 779094 15.83%
Alaska 710231 733391 3.26%
District of Columbia 601723 689545 14.60%
Vermont 625741 643077 2.77%
Wyoming 563626 576851 2.35%


INFO: You can create info, success, and warning alerts.
SUCCESS: Congrats on doing something.
WARNING: Don't forget to include the alert.type right before the alert.content

You can create info, success, and warning alerts. For now the syntax for an info alert is to write {{#alert.type}}info{{/alert.type}} and then wrap the content inside of {{#alert.content}} and {{/alert.content}}. Replace the word "info" with "success" or "warning" for green or red alerts.


You can create custom components from the templates page. Click the Add New button in the left sidebar and pick a name. Then enter the mustache template in the new textarea66 The textarea should automatically be syntax highlighted. with that name. If you don't know mustache syntax, go here.

Then simply add a block anywhere by wrapping a string or JSON object inside of {{#custom}} and {{/custom}} where the word custom needs to be replaced by the name of your block.

If you just add a string to your block then you need one or more {{input}} blocks inside of your component template where that text will go. If you include a JSON object then the keys will pair up as expected. Using {{.}} is the mustache syntax for an item in an array.

As an example, here is a simple to-do list component. Create a component named todo and then add the following template to the todo textarea.

<div style="width: 50%; display: block; margin: auto;">
<div style="border: 1px solid black;">
    <center><b>{{ title }}</b></center>
<div><input type="checkbox" />{{.}}</div>

Then add blocks like the following anywhere you want a checklist.

"title": "Steps to add custom component",
"items":["Enter name on template page.",
        "Add template for component.",
        "Add object block wherever you want."]

The result will be a simple checklist:

Steps to add custom component
Enter name on template page.
Add template for component.
Add object block wherever you want.


You can click the Settings button in the left sidebar to edit a few options.


Click the Template button in the top left to edit the HTML template. You can change the template itself by pressing the Input button above the template textarea. The {{ content }} block will be where all of your content goes and the {{ toc }} block will be where your table of contents ends up.

Template blocks can be added by including the appropriate mustache syntax. The content of the blocks can be edited by clicking the name of the block in the left sidebar. The base template should automatically be syntax highlighted, but any template blocks will be normal textareas.

Template blocks

You can add a title or description to your page by entering the desired text in the title or description textarea. You can add stylesheets by including a list of urls in the stylesheets textarea.

If you change the template, then any additional blocks will generate new textareas that you can edit as desired. You can add a block for scripts, a navigation menu, etc. A block can either require a string or a json object/array. Including functions within your objects will cause parsing errors.

When you save, the template is saved along with it so that you can save a template and then load it for each new article.


There are a few options for your preview mode that can be changed by clicking the settings button in the top left of the preview page. To get the best visual idea of what the final web page will look like, view the preview in an iframe by leaving the frame checkbox checked. If you want to check that internal links like the table of contents or endnotes work as intended you will need to uncheck to exit frame mode.

When using an iframe, you can set the width and/or height of the frame to see what your page looks like across different screen sizes. You can zoom out to see resolutions larger than your actual screen. If you do zoom out, the text and everything will be smaller than reality but there isn't really a way to exactly emulate a larger screen. The relative locations of everything should be accurate.

If you use relative links to images then they will not appear in the preview.

Custom Install

Download the zip to open up more customization options or keep data on your device. The code is GPLv3 licensed. The logic is currently not super well organized or documented to be easily customized, but most things are quite simple to adjust if you know where to look.

The rendering runs through a modified version of Mustache and a modified version of Marked. These renderers play fairly well together but it is not always obvious how the final markup is produced. Replacing Mustache with a custom templating system is probably going to happen at some point.

Custom CSS

Customizing the CSS isn't too hard. There is one CSS file (default.css) and it shouldn't be too hard to figure out which class to modify for simple changes. Structural changes to the positioning of things like side containers might require some tinkering to get right.

Custom Templates

Adding a custom default template is relatively simple. Just go to the templates.js page and change the text of the downloadTemplateIn variable. On that same page you can edit the function that generates the table of contents by changing the tocfn function.

Custom Components

The components.js file has the logic for converting the components into HTML. The key at the top level of allComponents is the tag name. Copying from an existing component is the easiest thing to do. For more information, look at the mustache docs.


This writer is still experimental and breaking changes are possible. UI improvements, documentation improvements, and bug fixes will happen gradually.

The focus for the near future will be making it easier to customize and use. More and better template options will be added as they are developed.

1 An endnote looks like this.