WebAssembly is a relatively new technology that allows building native apps for the web, which can provide extra performance, lower memory footprint, and improved security over traditional JS based web applications. In this tutorial we’ll walk through building a simple WebAssembly based application. The app is a simple text based chat application. For this tutorial we’ll use Alusus programming language, which provides good support for WebAssembly.
Requirements
We’ll keep the requirement simple for the sake of this tutorial. When the user launches the app
he/she should be presented with a dialog where you enter a username, a user password, a room name, and a room password. Once the user enters those info, we’ll log the user into that room. Like any other chat app, the room has an entry box where the user enters a new message, and an area for showing chat messages from all members of that room.
All you need in terms of development environment is to install Alusus language in any modern linux distro, and have any text editor.
Basic Chat Frontend
We will start off by building a basic UI. With Alusus and WebPlatform you build everything in Alusus itself without the need to embed any HTML or CSS; those will be generated for you by the compiler, so you build the app as if you are building a desktop application using some UI toolkit.
We will create two separate components, one for the header and one for the text entry, and we will have one main endpoint that displays the UI. Here is how the full code looks like:
import "Srl/String";
import "Apm";
Apm.importFile("Alusus/WebPlatform");
use Srl;
use WebPlatform;
//==============================================================================
// Frontend Components
class Header {
@injection def component: Component;
handler this~init() {
this.view = Box({}).{
style.{
padding = Length4.pt(4);
borderWidth = Length4.pt(0, 0, 1.5, 0);
borderStyle = BorderStyle.SOLID;
borderColor = Color("000");
justify = Justify.START;
display = Display.FLEX;
layout = Layout.COLUMN;
background = Background(Color("fff"));
};
addChildren({
Text(String("Wasm Chat")).{
style.fontSize = Length.pt(18.0);
}
});
};
}
handler this_type(): SrdRef[Header] {
return SrdRef[Header].construct();
}
}
class TextEntry {
@injection def component: Component;
handler this~init() {
def self: ref[this_type](this);
this.view = Box({}).{
style.{
display = Display.FLEX;
layout = Layout.ROW;
justify = Justify.SPACE_BETWEEN;
};
addChildren({
TextInput().{
style.{
flex = Flex(1);
margin = Length4.px(10);
height = Length.px(50);
fontSize = Length.pt(12.0);
borderStyle = BorderStyle.SOLID;
borderColor = Color("000");
borderWidth = Length4.px(1.5);
};
},
Button(String("Send")).{
style.{
height = Length.px(50);
width = Length.px(50);
fontSize = Length.px(16.0);
justify = Justify.CENTER;
margin = Length4.px(10, 10, 10, 0);
};
}
});
};
}
handler this_type(): SrdRef[TextEntry] {
return SrdRef[TextEntry].construct();
}
}
//==============================================================================
// Frontend Pages
@uiEndpoint["/"]
@title["Wasm Chat"]
func main {
Window.instance.style.{
padding = Length4.pt(0);
margin = Length4.pt(0);
};
Window.instance.setView(Box({}).{
style.{
height = Length.percent(100);
justify = Justify.SPACE_BETWEEN;
display = Display.FLEX;
layout = Layout.COLUMN;
background = Background(Color("aaa"));
};
addChildren({
Header(),
Text(String()).{
style.{
flex = Flex(1);
width = Length.percent(100) - Length.px(20);
margin = Length4.px(10, 10, 0, 10);
fontSize = Length.pt(20.0);
borderStyle = BorderStyle.SOLID;
borderColor = Color("000");
borderWidth = Length4.px(1.5);
background = Background(Color("fff"));
};
},
TextEntry()
})
});
runEventLoop();
}
//==============================================================================
// Entry Point
Console.print("Starting server on port 8000...\nURL: http://localhost:8000/\n");
buildAndRunServer(Array[CharsPtr]({ "listening_ports", "8000", "static_file_max_age", "0" }));
The code itself is pretty self explanatory; the UI endpoint is a function marked with the @uiEndpoint
modifier. In this function we are doing two things: setting the main view then entering the event loop. The view is a Box
, which is equivalent to a div
in HTML, and in that box there is the header, a text view for viewing chat history, and the text entry component. The Text
and Header
components are simply classes that inherit from WebPlatform’s Component
class and create the component’s view in the constructor.
If you are familiar with CSS you will recognize the styles we are assigning to each of the widgets. Each of these styles is equivalent to its HTML counterpart, but are defined in a strongly typed manner, and hence you’ll see the use of enumerations and classes instead of plain strings.
Put all that code in a single file called chat.alusus
, then run it like this:
alusus chat.alusus
Here is how the page looks like:
The UI currently does nothing as it’s not linked to any backend yet. This is what we will be doing in the next article.