loading...
Cover image for JSON::Presenter - a scripting language for web-based presentations

JSON::Presenter - a scripting language for web-based presentations

gtanyware profile image Graham Trott Updated on ・10 min read

Note on June 23 2020: This project is obsolete, having been replaced by I Wanna Show You (IWSY). Much of what follows is still valid but the code in IWSY is more relevant. A demo is at https://iwannashowyou.com/demo.html and the source code repository is at https://github.com/easycoder/easycoder.github.io/tree/master/iwsy.

For over 30 years PowerPoint has been the undisputed leader when it comes to presentations. Love it or hate it - and many do both - it has few rivals. However, although still the de facto standard it's a product from a former age that shows its age in the modern, dynamic world of the Web. For example, while offering a wide range of graphical features it comes up short where animation is concerned. It's quite hard to put together a presentation that involves a lot of revealing, hiding and moving blocks, and anything more sophisticated is either a world of pain or a complete non-starter.

And although you can embed PowerPoint presentations in a web page you have to export them specially into iframes, which is a rather clunky procedure. Most of us live in a browser these days, so how about something that's web-based all the way?

For the whole of my working life I have been obsessed with language; terminally unsatisfied with most of what's on offer and always driven to create something better. Well, different, at any rate. So I thought, "How hard can it be to create a presentation language and an engine to run it?"

A presentation is an array of steps, with little if any need for much in the way of control flow management, variables or other key elements of computer languages. As soon as I started to look for a suitable container format, JSON leapt out as being the obvious choice. (20 years ago it would have been XML but the world has moved on.) The object structure of JSON makes it well-equipped to handle a variety of language types, particularly highly sequential ones as is the case with presentations.

So I devised a new language - a JSON script format organized as a list of steps with meta-information to accompany it. For testing I wrote an interpreter in vanilla JavaScript. The two developed - and are still developing - side-by-side.

Example

I'll describe the language by means of an example. Here's a short demo presentation that exercises the currently-implemented features. The example is a bit contrived - often less is more - but I'm illustrating what is possible rather than what may be desirable. Just because features exist doesn't mean we have to use them.

https://easycoder.github.io/presenter/archive/index.html

When you load the page it will display a blank screen with a helpful message (to be replaced eventually with a splash screen) and wait for you to press a key.
Alt Text
Space and Right Arrow move the presentation to the next step. Enter puts it into 'auto' mode where it runs with its own timing. In this mode the cursor is hidden. If you click/tap the screen you will return to 'manual' mode after the current step completes.

The program also supports control by a mouse or touchscreen. Click/tap anywhere on the screen to start the presentation or to advance it to the next step.

If you want to run the presentation full-screen, start it up normally then switch your browser to full-screen mode. You may need to adjust the aspect ratio values in the script (see below) to fill the screen properly; I find 160:89 suits my Slimbook but you can experiment to see what works best for you.

The story: When the presentation starts it shows a black panel and waits a couple of seconds. It fades up a large colored intro title across the middle of the panel, then waits a few more seconds. Then it slides the title up the screen, reducing it in size and changing the text color as it goes. As soon as this arrives at the top, another panel fades up below it with the main text for this "slide" (a bit of a misnomer from the language of PowerPoint, since slides are an arbitrary concept; everything is really just steps). The screen holds for a while then continues to play around with text and images, finally fading everything down to black.

The files for this article can all be found at the JSON::Presenter source code repository.

Fundamentals

A presentation comprises 3 components:

  • Block structure - text and image boxes
  • Content - text and images to fill the boxes
  • Sequence - the steps that comprise the presentation

The design of JSON::Presenter keeps these 3 components separate, honoring the principle of "separation of concerns" and allowing each part to be handled independently. The items in Blocks and Content all have names that are used in the Sequence.

The language

A presentation script starts by defining some global values:

{
    "title": "JSON::Presenter",
    "description": "A demo presentation that outlines some features of JSON::Presenter",
    "aspectW": 16,
    "aspectH": 9,
...

title - a title, such as for the browser address bar
description is just for us human beings
aspectW and aspectH control the proportions of the window. The presentation engine uses an empty DIV provided by the page; this will govern the width of the presentation. The aspect ratio values are used to compute the corresponding height. Typical values are 16:9, 4:3 and 1:1, corresponding to landscape, old-style TV or square formats.

The rest of the script is made up of 5 sections:

container

This contains CSS style properties specific to the container itself:

    "container": {
        "border": "1px solid black",
        "background": "black"
    },

If you prefer to use regular CSS classes to style your container, just leave this section empty.

defaults

The system handles a fixed set of properties, all of which are given initial default values, as in

    "defaults": {
        "fontFamily": "Times New Roman,serif",
        "fontSize": 40,
        "fontWeight": "normal",
        "fontStyle": "normal",
        "fontColor": "white",
        "textAlign": "left",
        "textMarginLeft": 0,
        "textMarginTop": 0,
        "blockLeft": 0,
        "blockTop": 0,
        "blockWidth": 1000,
        "blockBackground": "none",
        "blockBorder": "none",
        "blockBorderRadius": 0
    },

As you will already have guessed, the system calls upon CSS to do all the heavy lifting. Many of the values you see are camel-case versions of CSS styles.

For things to work properly in browsers, the content must be able to deal with any screen size or resolution. True responsiveness, in the sense of adjusting itself on the fly to window resizing, may not be needed, but when the presentation starts up it must be able to cope with whatever environment it finds itself in. JSON::Presenter handles this by using "mils" rather than pixels. A "mil" is like a percentage, but with 1000 steps instead of 100. The presentation container is regarded as being 1000 mils wide and 1000 mils high, so an object that is defined as being 333 mils wide occupies one third the panel width. Mils are used for sizing and positioning on-screen blocks, for margins and for text sizing.

blocks

A presentation comprises a number of blocks containing either text or images. Presentations tend to use a common layout for a large number of slides, so it helps if these are all declared up front as templates that can be used as needed. This avoids "jumps" when the flow moves from one step to the next. The blocks section for this demo looks like this:

        "title": {
            "blockTop": 300,
            "blockHeight": 300,
            "textAlign": "center",
            "fontSize": 200,
            "fontWeight": "bold",
            "fontColor": "#800000"
        },
        "title 2": {
            "blockTop": 50,
            "blockHeight": 150,
            "textAlign": "center",
            "fontSize": 100,
            "fontWeight": "bold",
            "fontColor": "#dddd00"
        },
        "body": {
            "blockLeft": 80,
            "blockTop": 240,
            "blockWidth": 840,
            "blockHeight": 800,
            "fontFamily": "Helvetica,sans-serif",
            "fontColor": "#dddddd"
        },
        "body right": {
            "blockLeft": 500,
            "blockTop": 200,
            "blockWidth": 420,
            "blockHeight": 800,
            "fontFamily": "Helvetica,sans-serif",
            "fontColor": "#dddddd"
        },
        "left image": {
            "blockLeft": 80,
            "blockTop": 200,
            "blockWidth": 370,
            "blockHeight": 700,
            "blockBorder": "1px solid black",
            "blockBorderRadius": "1em"
        }
    },

This defines 5 blocks. One is the initial title, the second is the smaller title at the top of the screen and the third is the container for the body text. Then we have a second body block and an image. Note that no content is defined here, just the size, position and other values such as text style and color. The name of each block will be used by the step engine.

The properties are all those listed in the defaults section. Blocks take on all of the default browser properties, except where overridden first by the defaults, then in the block definition.

content

This section defines all the text and image URLs used in the presentation.

    "content":  {
        "presenter title": {
            "type": "text",
            "content": "JSON::Presenter"
        },
        "slide 1": {
            "type": "text",
            "content": [
                "JSON::Presenter is a presentation format using JSON scripts, and an engine that runs those scripts in a browser to create presentations. These may be similar to those created using PowerPoint or they can be considerably more elaborate, with extensive animation and even sound. In some cases they can take the place of video yet still offer a dynamic experience.",

                "Presentations can run from any host, including static; all you need is one CDN-hosted JavaScript file and you're good to go.",

                "The JSON::Presenter engine is pure JavaScript. It can be used with any JavaScript framework, or with none."
            ]
        },
        "slide 2": {
            "type": "text",
            "content": [
                "JSON::Presenter offers a range of block types and transitions that make it easy to create slick, effective presentations.",

                "This short demo illustrates some of the features of the system."
            ]
        },
        "slide 3": {
            "type": "text",
            "content": [
                "Text and image blocks can be manipulated in a variety of different ways.",

                "Any block can be resized or moved; text can be substituted or have its size or color change; images can be assigned to blocks. Any block can be faded or transformed using animations.",

                "The JSON::Presenter scripting language uses simple data JSON structures and is easy to read or write."
            ]
        },
        "flowers": {
            "type": "image",
            "url": "img/flowers.jpg"
        },
        "moon": {
            "type": "image",
            "url": "img/moon.jpg"
        }
    },

Here we have several items; a title, some arrays of paragraphs and a couple of images. Paragraph arrays deal with the problem that line breaks can't be included in JSON content without using escaped characters. The system handles a simple string and a paragraph array interchangeably. Note that each item has a name; this will be used by the step processor.

Each item has a type property that tells the system about the content. Here there are both text blocks and images. Other types may exist, such as video or executable code.

steps

The final section is the list of steps that control the presentation. Note that the syntax below may have changed since the publication of this article; the only way to be sure of the current syntax is to visit the source code repository.

    "steps": [
        {
            "comment": "------------------------------- Pause before we start",
            "action": "pause",
            "duration": 2
        },
        {
            "comment": "---------------------------------- Set up the content",
            "action": "set content",
            "blocks": [
                {
                    "block": "title",
                    "content": "presenter title"
                },
                {
                    "block": "body",
                    "content": "slide 1"
                },
                {
                    "block": "left image",
                    "content": "flowers"
                }
            ]
        },
        {
            "comment": "----------------------------- Fade up the intro title",
            "action": "fade up",
            "blocks": "title",
            "duration": 3
        },
        {
            "comment": "-------------------------------------- Wait 4 seconds",
            "action": "hold",
            "duration": 4
        },
        {
            "comment": "-------------------------------- Transition the title",
            "action": "transition",
            "type": [
                "block position",
                "block size",
                "font color",
                "font size"
            ],
            "block": "title",
            "target": "title 2",
            "duration": 1,
            "continue": true
        },
        {
            "comment": "----------------------------- Pause for half a second",
            "action": "pause",
            "duration": 0.5
        },
        {
            "comment": "-------------------------------- Show the first slide",
            "action": "fade up",
            "blocks": "body",
            "duration": 1
        },
        {
            "comment": "------------------------------------- Wait 10 seconds",
            "action": "hold",
            "duration": 10
        },
        {
            "comment": "-------------------------------- Change the body text",
            "action": "crossfade",
            "block": "body",
            "target": "slide 2",
            "duration": 1
        },
        {
            "comment": "-------------------------------------- Wait 5 seconds",
            "action": "hold",
            "duration": 5
        },
        {
            "comment": "-------------------------- Move the body to the right",
            "action": "transition",
            "type": [
                "block position",
                "block size"
            ],
            "block": "body",
            "target": "body right",
            "duration": 1
        },
        {
            "comment": "----------------------------- Fade up the image block",
            "action": "fade up",
            "blocks": "left image",
            "duration": 2
        },
        {
            "comment": "-------------------------------------- Wait 8 seconds",
            "action": "hold",
            "duration": 8
        },
        {
            "comment": "--------------------------------- Crossfade the image",
            "action": "crossfade",
            "block": "left image",
            "target": "moon",
            "duration": 1
        },
        {
            "comment": "-------------------------------------- Wait 2 seconds",
            "action": "hold",
            "duration": 2
        },
        {
            "comment": "-------------------------------- Change the body text",
            "action": "set content",
            "block": "body",
            "content": "slide 3"
        },
        {
            "comment": "------------------------------------- Wait 10 seconds",
            "action": "hold",
            "duration": 10
        },
        {
            "comment": "------------------------ Fade down the title and body",
            "action": "fade down",
            "blocks": [
                "title",
                "body",
                "left image"
            ],
            "duration": 3
        }
    ]
}

This section is an array. In this example, every element has a comment item for the benefit of human readers; you can "read" the script by looking down the right-hand side of the page. Every element also has an action property that defines what the step has to do.

Starting at the top we have a create action. This can either create a single block or all of the blocks named in an array, as happens here.

Block creation involves adding new div elements to the DOM. This demo only uses one of the title blocks; the other is just there to provide a set of properties for use in a transition, but it never gets created as a div. Likewise for the second body and image blocks.

The second step assigns text to the two created text blocks and the first image block. As you can see, arrays allow any number of blocks to have their content set in a single step.

The third step pauses the presentation for 2 seconds before starting. The seconds value can be a decimal number if you wish.

Now we fade up the title over 3 seconds.

We have another pause while the viewer admires the title, then into the most complex part of this example. The text is to move, shrink in size and change color, all at the same time. This is done in a single transition action, which defines the 4 transition types we want to use. Transitions occur between the current block and any other block; the latter provides all the values for the end of the transition but plays no other part in its execution. The overall transition may not be completely smooth, but this is hardly surprising given that 4 sets of new CSS values are being computed and delivered 25 times a second.

With all fades and transitions, the default behavior is for the next step to wait until the animation has completed before it begins. By adding "continue": true to the properties we override this behavior. In this example I want the main body text to start to appear while the title text is still moving. The transition is set to take a second, so I follow this with a half-second pause before starting to fade up the body text. This also takes a second so the fade starts half-way through the transition and ends half a second after it finishes. A great deal of aesthetic 'fine tuning' can be achieved in this way.

The demonstration then continues by showing images and moving or replacing text - the kinds of things you'd expect from any presentation system.

The second half of this article describes a JavaScript engine built to run these presentations.

Photo by Charles Deluvio on Unsplash

Posted on by:

gtanyware profile

Graham Trott

@gtanyware

Software Engineering relic with a keen interest in making programming more accessible to ordinary people.

Discussion

pic
Editor guide
 

This was great! Thanks for sharing your work and expertise with us Graham 😊