A JavaScript engine for running JSON-formatted presentation scripts.
This project started with a desire to create presentations that could run from a static website. I devised a JSON format (subject to change at this point) to define the structure, content and progress of a presentation, and wrote a JavaScript engine that runs any such script to create the presentation.
This is not a PowerPoint emulation. Among my aims are to include animation features such as marquees or text that appears to be typed live, and embedded program code to allow interctivity during the presentation. This would allow the creation of storyboards that propose a given look and feel, without the need to build an actual product prototype. Such presentations would not require any programming (except for added program code), just the ability to manage JSON.
The JSON::Presenter project is in an early stage of development. Comments and enquiries are welcome.
For an overview of the project see this article on dev.to.
The simplest is to start with the demo script here and adapt it to your needs. A script has 6 groups of properties:
- Global
- Container
- Defaults
- Blocks
- Content
- Steps
title
is a string that may be used by the presentation engine in the title bar of the browserdescription
is not used by the engine; it's for human readabilityaspectW
andaspectH
together determine the height of the container, computed aswidth * aspectW / aspectH
These relate to the container. There are currently only 2 values:
border
is the CSS style that defines the border of the containerbackground
defines the background style to apply to the container
If you prefer to use separate CSS classes to style your container, leave this section empty.
Every block used in the presentation has its own style properties.To minimize the amount of repetition, values that are used repeatedly can be kept in this section. When a block is created it will inherit styles from its container; these will be overridden if defined here. Finally, styles defined for the block itself are applied. So if your text is always blue, set a blue default value for textColor
.
The default values currently recognized are
fontFamily
fontSize
fontWeight
fontStyle
fontColor
textAlign
textMarginLeft
textMarginTop
blockLeft
blockTop
blockWidth
blockBackground
blockBorder
blockBorderRadius
Some of these, e.g. textAlign
and fontWeight
, are just regular CSS values reperesnted with camel case. All the items that represent sizes, however, have values given in mils - that is, thousandths of the width or height of the presentation container. This ensures they will display properly on any device.
For example, if a block is to be given a left position that's one-fifth (20%) of the container width, use
"blockLeft": 200
This applies to the height of text, too. Although for most of the rest, mils are the same as fractional percentages, if you were to use a percentage for text it would relate the height to the current font size, which would not be the same thing at all.
If you wish you can use CSS units like px
, pt
, em
or %
where appropriate, though you are likely to have trouble applying transitions.
A presentation is composed of blocks of text or images, having positions and sizes that are usually repeated for many slides, with only the content changing. JSON::Presenter requires you to define these blocks at the start, then provides you with the means to manipulate them during the presentation.
A typical block looks like this:
"title": {
"blockTop": 300,
"blockHeight": 300,
"textAlign": "center",
"fontSize": 200,
"fontWeight": "bold",
"fontColor": "#800000"
},
The only properties that are needed are ones that differ from those in the defaults
section.
The block definition is just a specification and does not cause a block to be created. This only happens when the block is given content in a set content
step. Some blocks are never created; they are used in transition effects to tell the system what the endpoint of the transition looks like.
The name of each block is used in the steps
section to refer to that block.
This section contains all the text and image URLs that will be used by the presentation. Rather than have this information scattered among many slides it its all kept in one place, and as with blocks, each item is given a name so it can be accessed when needed.
Text comes in two forms; fistly as a simple string and secondly as an array of strings. Either can be used in any placce that takes a string. In an array, each of the items is a separate paragraph, which avoids the need to use escaped characters in the JSON definitions.
This is the presentation proper.The engine runs through the list in linear order, applying the action specified for each step. The action
property is the key; all the others vary according to the action. The comment
property is for documentation; although optional it is highly recommended as it provides an easy way to to follow the script
Every step has an action
property from this list:
set content
show
hide
pause
hold
fade up
fade down
crossfade
transition
goto
load
Use this action to set content into one or more blocks. For a single block use this:
{
"comment": "---------------------------------- Set up the content",
"action": "set content",
"blocks": "body",
"content": "slide 1"
},
and for multiple blocks, use this:
{
"comment": "---------------------------------- Set up the content",
"action": "set content",
"blocks": [
{
"block": "title",
"content": "presenter title"
},
{
"block": "body",
"content": "slide 1"
},
{
"block": "left image",
"content": "flowers"
}
]
},
In all cases the blocks and the content are identified by their names.
When this action runs the block(s) named are constructed as div
elements and added to the container. There is currently no specified z-indexing as visibility is determined by opacity. This means that if you want to overlay one block on another (where both will be at least partially visible) you must declare it later than the one it overlays.
Shows one or more blocks, i.e. makes them visible. This also takes 2 different forms:
{
"comment": "-------------------------------------- Show the title",
"action": "show",
"blocks": "title"
},
and
{
"comment": "------------------------------------ Show some blocks",
"action": "set content",
"blocks": [
"title",
"body",
"left image"
]
},
Hides one or more blocks, i.e. makes then invisible. See show
.
Pause the presentation for the duration given (in seconds).
{
"comment": "------------------------------- Pause before we start",
"action": "pause",
"duration": 2.5
},
This is like pause
except that in manual mode the presentation will stop and wait for the user to press a key or click/tap. The duration
property has no effect in this case. You can use a duration of 0 if you just want to provide for user action (like a slide change in PowerPoint).
{
"comment": "------------------------------------ Wait for a while",
"action": "hold",
"duration": 2.5
},
Fade up one or more blocks. For a single block:
{
"comment": "----------------------------------- Fade up the title",
"action": "fade up",
"blocks": "title",
"duration": 3
}
and for multiple blocks:
{
"comment": "-------------------------- Fade up the title and body",
"action": "fade up",
"blocks": [
"title",
"body",
"left image"
],
"duration": 3
}
Fades run at 25 updates per second. In this example there will be 75 steps.
Fade down one or more blocks. See fade up
.
Fade down the content of a block and at the same time fade up new content. This is often more useful than running a fade up
and a fade down
concurrently, as the block ending up being visible is the same as before.
The target
value is a content
item that must be of the same type as that currently in the block.
Unlike fade up
and fade down
, only one block can be specified by this action.
{
"comment": "--------------------------------- Crossfade the image",
"action": "crossfade",
"block": "left image",
"target": "moon",
"duration": 1
},
Crossfades run at 25 updates per second.
Perform one of several different transitions on a block. Currently only text blocks are supported. The transitions available are
block position
moves the block to a new locationblock size
makes the block bigger or smallerfont color
changes the color of text in a blockfont size
changes the size of text in a block
In all cases the new values are held in a target
block of the same type as the one being transitioned. This block will not be created during the transition. If it already exists and has content it will not be altered in any way.
{
"comment": "-------------------------------- Transition the title",
"action": "transition",
"type": [
"block position",
"block size",
"font color",
"font size"
],
"block": "title",
"target": "title 2",
"duration": 1,
"continue": true
},
In this example the title
block is moved and resized and its text size and color are changed, all concurrently, where the ending values are provided in a block called title 2
that need not have any content and therefore does not exist in the DOM.
Transitions run at 25 updates per second.
Go to a specified step. The syntax is
{
"comment": "-------------------------------- Go back to the start",
"action": "goto",
"step": "start"
}
The target of the goto
must have a label
property with a unique value, as in
{
"comment": "------------------------------- Pause before we start",
"action": "pause",
"duration": 2,
"label": "start"
},
Load a plugin extension to JSON::Presenter. This is a JavaScript file with a fixed format:
const JSON_Presenter_Test = step => {
...
};
JSON_Presenter.plugins.test = JSON_Presenter_Test;
The name of this action will be test
. The name of the plugin is recommended to start with JSON_Presenter
to avoid namespace clashes. The plugin runs when it is loaded and installs itself into the plugins
property of the main program. It will then be available as an action under the name given. To supply it with options, add these as properties of the step
.
This example fades a block down then back up again in the space of one second:
const JSON_Presenter_Test = step => {
const animSteps = Math.round(step.duration * 25);
let animStep = 0;
let interval = setInterval(() => {
if (animStep < animSteps) {
const ratio = 0.5 - Math.cos(Math.PI * animStep / animSteps) / 2;
const block = step.script.blocks[step.block];
block.element.style[`opacity`] = 1.0 - ratio;
animStep++;
} else {
clearInterval(interval);
animStep = 0;
interval = setInterval(() => {
if (animStep < animSteps) {
const ratio = 0.5 - Math.cos(Math.PI * animStep / animSteps) / 2;
const block = step.script.blocks[step.block];
block.element.style[`opacity`] = ratio;
animStep++;
} else {
clearInterval(interval);
step.next();
}
}, 40);
}
}, 40);
};
JSON_Presenter.plugins.test = JSON_Presenter_Test;
The plugin takes a single step
argument; this provides access to everything else inside the JSON::Presenter environment. Here the value duration
will bbe assumed as a property of step
.
The step in your presentation script will look like this:
{
"comment": "--------------------------------- Run the test plugin",
"action": "test",
"block": "title",
"duration": 0.5
}
The format of the load
action is
{
"comment": "-------------------------------- Load the test module",
"action": "load",
"url": "test.js"
}
where the url
property is the full URL needed to access the file.