Home

Awesome

<p align="center"> <a href="#"> <img src="https://i.imgur.com/FBN4t6x.png" height="118"> </a> <br/> <br/> <i>azula</i>, a lightweight GPU accelerated HTML GUI for native JavaScript applications <br/> <br/> <a href="https://www.npmjs.com/package/azula"> <img src="https://img.shields.io/npm/v/azula.svg?style=flat-square" alt="NPM Version" /> </a> <br/> <br/> <img src="https://i.imgur.com/bfwxryC.gif" /> </p>

‌‌

azula is a lightweight alternative to Electron. It is based on Ultralight, which is an embedding friendly Fork of WebKit, with less memory usage and low disk space requirements.

azula can optionally run in OSR mode, which makes it easy to embed azula in existing Projects like Game/VR Engines.

Characteristics

AzulaElectron
CPU1.2%4.2%
RAM37Mb64Mb
DISK31Mb118Mb

Platforms

azula comes with pre-built N-API binaries for the following platforms:

OSStatus
<img src="https://i.imgur.com/FF3Ssp6.png" alt="" height="16px"> Windows‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ✔ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌
<img src="https://i.imgur.com/bkBCY7V.png" alt="" height="16px"> Linux‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ In Progress ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌
<img src="https://i.imgur.com/iPt4GHz.png" alt="" height="16px"> MacOS‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ In Progress ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌

Getting Started

Install azula using:

npm install azula

You can now import azula into your project:

const azula = require("azula");

Or with ESM:

import azula from "azula";

API

Window

When creating a new Window, the following parameters are available:

NameTypeDescription
width (Optional)NumberThe initial width of the window
height (Optional)NumberThe initial height of the window
title (Optional)StringThe initial title of the window
useOffscreenRendering (Optional)BooleanWhen true, creates the window in OSR mode
let window = new azula.Window({
  width: 480,
  height: 320,
  title: "My App",
  useOffscreenRendering: false
});

General

Window.prototype.title

TypeDescription
StringA getter/setter allowing to retrieve or update the title of the window
window.title = "My App";
window.title; // "My App"

Window.prototype.width

TypeDescription
NumberA getter/setter allowing to retrieve or update the width of the window
window.width = 640;
window.width; // 640

Window.prototype.height

TypeDescription
NumberA getter/setter allowing to retrieve or update the height of the window
window.height = 480;
window.height; // 480

Window.prototype.update

This method should be called to poll window events (making the window interactive). In non-OSR mode, this method also does the painting of the window.

window.update();

Window.prototype.flush

This method should only be used in OSR mode. Calling this method executes all remaining render operations and flushes the underlying context.

window.flush();

Window.prototype.shouldClose

TypeDescription
BooleanA boolean, indicating if the window should be closed
window.shouldClose(); // true/false

Loading

Window.prototype.loadHTML

NameTypeDescription
htmlStringString representation of the HTML to load
window.loadHTML("<button>Hello World!</button>");

Window.prototype.loadFile

NameTypeDescription
pathStringThe path from where the content gets read from
window.loadFile("./index.html");

Events

Window.prototype.onresize

TypeDescription
FunctionThe function to call when the window gets resized

The callback's Event parameter has the following structure:

NameTypeDescription
widthNumberThe new width of the window
heightNumberThe new height of the window
window.onresize = e => {
  console.log(e.width, e.height);
};

Window.prototype.oncursorchange

TypeDescription
FunctionThe function to call when the cursor should be changed

The callback's Event parameter has the following structure:

NameTypeDescription
nameStringA name representing the cursor type to change to
window.oncursorchange = e => {
  console.log(e.name);
};

Window.prototype.onconsolemessage

TypeDescription
FunctionThe function to call when a console message got sent

The underlying JavaScript engine of azula is WebKit's JavaScriptCore engine. Now this means, that the JavaScript running in the GUI is separated from the JavaScript in Node. When the JavaScript in the GUI makes a call to the console, e.g. console.log(42);, we have to route this call over to Node.

The callback's Event parameter has the following structure:

NameTypeDescription
levelStringThe level of the console call. For example "log", "warn" or "error"
calleeFunctionNode's equivalent console function to call
messageStringThe message passed to the console call
sourceStringThe file or location where the call was made. Is empty when loadHTML was used
locationObjectAn Object describing the exact code location where the console call was made from

The location Object comes with the following structure:

NameTypeDescription
lineNumberThe code line where the console call originated from
columnNumberThe code column where the console call originated from
window.onconsolemessage = e => {
  let location = `at ${e.source ? e.source + ":" : ""}${e.location.line}:${e.location.column}`;
  e.callee.apply(console, [e.message, location]);
};

Event Dispatching

The Event Dispatching System should only be used in OSR mode. Event Dispatching allows to manually send events to the GUI, such as mouse gestures or key events.

Window.prototype.dispatchMouseEvent

NameTypeDescription
typeStringThe type of event
xNumberThe horizontal position of the mouse
yNumberThe vertical position of the mouse
buttonNumberThe currently pressed mouse button

The following event types are available:

NameType
onmousedownSimulating a mouse press action
onmouseupSimulating a mouse leave action
onmousemoveSimulating a mouse move action
window.dispatchMouseEvent("onmousedown", 16, 32, 1); // press the left mouse button at 16:32
window.dispatchMouseEvent("onmouseup", 16, 32, 1); // leave the left mouse button at 16:32
window.dispatchMouseEvent("onmousemove", 16, 32, 0); // move the mouse to 16:32 without pressing a mouse button

Window.prototype.dispatchKeyEvent

Key Codes are mapped towards GLFW's Key Codes.

NameTypeDescription
typeStringThe type of event
keyCodeNumberA key code representing which key to press

The following event types are available:

NameType
onkeydownSimulating a key press action
onkeyupSimulating a key leave action
window.dispatchKeyEvent("onkeydown", x); // press a key
window.dispatchKeyEvent("onkeyup", x); // leave a key

Window.prototype.dispatchScrollEvent

NameTypeDescription
typeStringThe type of event
deltaXNumberThe horizontal amount to scroll
deltaYNumberThe vertical amount to scroll

The following event types are available:

NameType
onmousewheelSimulating a mouse wheel action
window.dispatchScrollEvent("onmousewheel", 0, 1); // scroll upwards, vertically by 1
window.dispatchScrollEvent("onmousewheel", -1, 0); // scroll downwards, horizontally by -1

Object Messaging

The underlying JavaScript engine of azula is WebKit's JavaScriptCore engine. The JavaScript engine of Node is different to the one used in azula, so we cannot directly exchange data. The Object Messaging System allows to send Object between both engines.

Note that to be sent Objects should kept small, as behind the scenes, they get serialized.

Window.prototype.dispatchObject

An equivalent method is available in the GUI. See this example as a reference.

NameTypeDescription
objectObjectThe Object to send to the GUI
window.dispatchObject({ message: "PING" });

Window.prototype.onobjectmessage

An equivalent method is available in the GUI. See this example as a reference.

TypeDescription
FunctionThe function to call when an object message was sent from the GUI

The callback's Event parameter has the following structure:

NameTypeDescription
objectObjectThe Object sent from the GUI
window.onobjectmessage = object => {
  console.log(object);
};

Binary Messaging

The underlying JavaScript engine of azula is WebKit's JavaScriptCore engine. The JavaScript engine of Node is different to the one used in azula, so we cannot directly exchange data. The Binary Messaging System allows to efficiently pass ArrayBuffers between both engines. Even though the engines are different, ArrayBuffers can be exchanged without any copying, meaning they don't have any overhead.

Window.prototype.dispatchBinaryBuffer

An equivalent method is available in the GUI. See this example as a reference.

The binarymessage system should only be used when sending large data between Node and azula. The buffer argument is a referenced buffer, which means there is no overhead when sending it between Node and azula as the data is effectively referenced.

The second argument is an Object (and is optional), which can be used to give some additional information about the buffer argument. This Object should be kept small, as it gets serialized behind the scenes, and so comes with some overhead.

NameTypeDescription
bufferArrayBufferThe ArrayBuffer to send to the GUI
args (Optional)ObjectAn Used-defined Object providing additional information about the buffer
window.dispatchBinaryBuffer(new ArrayBuffer(16), { kind: "SOME_DATA" });

Window.prototype.onbinarymessage

An equivalent method is available in the GUI. See this example as a reference.

The binarymessage system should only be used when sending large data between Node and azula. The buffer argument is a referenced buffer, which means there is no overhead when sending it between Node and azula as the data is effectively referenced.

The second argument is an Object (and is optional), which can be used to give some additional information about the buffer argument. This Object should be kept small, as it gets serialized behind the scenes, and so comes with some overhead.

TypeDescription
FunctionThe function to call when a binary message was sent from the GUI

The callback's Event parameter has the following structure:

NameTypeDescription
bufferArrayBufferThe ArrayBuffer sent from the GUI
args (Optional)ObjectAn Used-defined Object providing additional information about the sent buffer
window.onbinarymessage = (buffer, args) => {
  console.log(buffer, args);
};

OSR Mode

Window.prototype.getSharedHandleD3D11

TypeDescription
BigIntA BigInt representing a Windows HANDLE

On Windows, you can use this method to retrieve a shared HANDLE to the underlying D3D11 render texture.

let handle = window.getSharedHandleD3D11();

OSR

See this example as a reference.

azula supports running in OSR (Offscreen rendering) mode. This means, that instead of creating a window, an invisible texture gets used and rendered into. This texture can then be imported into a 3D engine for example. Another common use case would be, to display the texture in a VR environment.

On Windows, you can request a shared HANDLE using the Window's getSharedHandleD3D11 method.

License

Azula is MIT licensed, while Ultralight comes with the following License:

Ultralight is free for non-commercial use, educational use, 
and also free for commercial use by small indie developers making
less than US$100,000 a year. You can find full terms in the SDK. 
Pricing plans for larger commercial projects will be announced later.

For further information regaring the licensing of Ultralight, see this link.

‌‌

No, you miscalculated! You should have feared me more! - Azula