WASM has a linear memory to pass data between JavaScript (or any other hosting environment) and WASM. Any data which cannot be represented by WASMs data types (32 and 64bit integers and floats) also has to be passed through this memory.
In JavaScript we can access the WASM memory through a DataView object.
WebAssembly.instantiateStreaming(fetch("lib.wasm"), {}).then(
(obj) => {
const dv = new DataView(obj.instance.exports.memory.buffer)
//...
}
The DataView object allows us to read and write data to the memory. To pass a string to a WASM function we first have to encoded it to bytes and then write these bytes into the memory buffer. setUint8 sets the i-th byte of the memory buffer to the provided value.
// encode string in "program" as utf8 and write to WASM memory
let utf8Encode = new TextEncoder();
const p_data = utf8Encode.encode(program);
for (let i = 0; i < p_data.length; i++) {
dv.setUint8(i, p_data[i])
}
The byte position of the written data serves as the "pointer" to the data, that we have to pass to the WASM function call. As the data was written to the start of the memory it will be 0 in this case.
As we can only pass pointers, not strings directly, we also have to pass the length of our string. Otherwise the called function doesn't knowns how much data has to be read from the linear memory.
The called function calculates a RGB color value. The values are written to the provided result location, therefore we also have to provide a pointer for the result in the call. The first two parameters define the string we pass into the function, start location 0 and length of p_data.length. The third parameter is the result location. This is set to p_data.length as this is the first unused location in our memory, directly after the string.
After the function call we have to retrieve the data from the memory. In this case we know that the function will always write 3 floating point values. Otherwise the function would also need to return the length of the result.
The data is again retrieved throught the DataView object via the getFloat32 function. Note that the address is given as a byte offset and therefore has to be increased by 4 each time (byte size of a float). The second parameter defines the endianness of the read data.
const r = dv.getFloat32(p_data.length + 0, true);
const g = dv.getFloat32(p_data.length + 4, true);
const b = dv.getFloat32(p_data.length + 8, true);
This was a problem as the WASM target does not support the types []const u8 and [3]f32 as function parameter/return type. Therefore I've created a new function (creatively called render_point_2) to wrap my original implementation:
The program is now passed as a pointer to an array + its length. The function no longer has a return value, but writes the result to a provided array address. The caller has to ensure that the array can store 3 floating point values.
Also note the export keyword. I've missed this and spent an hour wondering why my WASM file had no exported function.
Building WASM
To build the WASM file you can either use the zig build-exe command or integrate it into your build.zig file.
The command zig build-exe src/root.zig -target wasm32-freestanding -fno-entry -rdynamic -OReleaseFast will create the WASM file in your project folder.
To build the WASM file via zig build add the following block to your builg.zig file. This will create the file in zig-out/bin. (based on this gist from trasstaloch )
I've created a small programming languages I've been thinking about for a long time. I wanted a programming language that would interpret any string as a valid program and create some visual result out of that. The inspiration was the hacker movie aesthetics, typing furiously on their laptops and bright, colorful patterns flashing over screens.
Design
The language is implemented as a stack based machine. Each character is interpreted as an operation on this stack. For example, a take the top two items from the stack, adds them up and puts the result back. Unassigned symbols are simply ignored.
The stack is initiated with the x and y coordinates of a pixel in an image. (I also put a time value, which is always zero, onto the stack as I want to extend the system for animations in the future).
The program is then executed for each pixel in an [512x512] image. When the program stops (this is guaranteed through the language), the top three items of the stack are interpreted as RGB values.
Implementation
The implementation of the languages itself is rather simple, as it's only a large switch-case statement.
As I wasn't sure how fast the interpreter would run when implemented JavaScript I've decided to built it in zig.
The zig program is split into a library and executable. The executable simply wraps the library by invoking the render_point function for each pixel in an image and saving the result to disk.
Usage via the command line was rather tedious. Therefore I've compiled the library to WASM to integrate it into an web app to allow for an interactive workflow.
As rendering a long program might take a few seconds I've split the rendering process into chunks to avoid blocking the UI. Randomizing the order of chunks added a nice hackeresque feel to the process.
To create the layout of the website I used Claude, as this was a part of the process I was not interested in. Everything else is organic, hand-crafted artisanal code.
Conclusion and Future
This was a fun project, which could be implemented within a few programming session. I've struggled a bit generating the WASM file. The zig documentation on this topic is rather slim and many examples are outdated. However, this was the first time I've worked with WASM and in the end it was a simple solution.
In the future I want to implement the animation feature of the language. For this I will also have to increase the performance. I want to try WebGL and SIMD for this. Another interesting topic might be to generate programs through algorithms. I think genetic algorithms could work well with this language as it can be mutated and crossed without concern for validity.
Mir fällt gerade auf, dass durch Integration von "KI Tools" Auf Betriebssystem Ebene die Infrastruktur geschaffen wird, die für die Chatkontrolle gefordert wurde.
Chatkontrolle
Da fast alle gängigen Messenger inzwischen Ende zu Ende verschlüsselt kommunizieren haben Sicherheitsbehörden es schwer Chats zu überwachen. Da auch die Betreiber der Chatprogramme keinen Einblick haben ist die einzige verbleibende Möglichkeit zu Überwachung das abfangen der Nachrichten auf dem Gerät der Nutzer. Dies benötigt zur Zeit den Einsatz eines "Staatstrojaners".
Da der Trojanereinsatz ein schwerer und kostspieliger Eingriff ist, versucht die Politik immer wieder eine Alternative zu schaffen. Der letzte Angriff war die sogenannten Chatkontrolle. Unter dem Vorwand der Kindesmissbrauchsbekämpfung sollen Chatanbieter gezwungen werden Chats nach dokumentiertem Kindesmissbrauch zu durchsuchen.
Technisch bedeuten diese Versuche, dass die Ende-zu-Ende Verschlüssung entweder entfernt oder umgangen werden muss. Anbieter könnten die Verschlüsselung anpassen um Daten auf ihren Servern zu analysieren oder alternative über einen zweiten Kanal zur Analyse ausspielen.
KI Tools
Im aktuellen GenAI Hype wird in derzeit in jede App ein "KI Funktion" eingebaut die nicht bei drei auf den Bäumen ist. Die Giganten Google and Microsoft gehen direkt einen Schritt weiter und bauen die KI direkt in ihre Betriebssystem ein.
Bei Microsoft heißt das "Feature" Recall und ist standardmäßig Teil von Windows 11. Recall macht alle 30 Sekunden einen Screenshot und speichert diesen in einer Datenbank. Die KI kann diese Datenbank durchsuchen und dem Nutzer so helfen Sachen wiederzufinden. Die Verarbeitung geschieht laut Microsoft ausschliesslich lokal auf dem eigenen Rechner.
Google integriert zur Zeit ihr KI System Gemini tief in das Android Betriebssystem. Es soll es der Benutzerin ermöglichen die KI über alle Apps hinweg zu benutzen. Ohne das die App selbst KI Funktionen implementiert. Dies bedeutet jedoch auch, dass Gemini auf die Daten in allen Apps zugreifen kann. Da die Daten "in der Cloud" verarbeitet werden, erlangt Google somit Zugriff auf Chats und Daten die vorher privat waren.
Die Entwicklung von KI Modellen benötigt massiv Daten. Aktuelle Modelle haben bereits alle frei zugänglichen Quellen leer gesaugt und benötigen Nachschub. Auch wenn die Systeme zur Zeit nicht genutzt werden um Trainingsdaten zu generieren, ist es nicht abwegig, dass sich dies schnell ändern wird. Im Wettkampf um die besten Modelle werden immer mehr Daten benötigt. Und Daten die Nutzerverhalten abbilden sind nicht erst im aktuellen KI Hype wertvoll geworden.
Auch OpenAI will Daten durch einen eigenen Browser Daten sammeln. Meta hatte noch nie ein augeprägtes Verständnis für Datenschutz und sammelt mit Facebook und Instagram fleißig mit.
Nur ein Schritt fehlt
Die Infrastruktur die man zum sammeln von KI Trainingsdaten und zur Überwachung ala Chatkontrolle benötigt unterscheidet sich nur in wenigen Details. In beiden Fällen benötigt man Funktionen die auf den Geräten der Nutzer Daten sammeln. Diese Daten müssen dann ausgeleitet werden um sie auf den eigenen Servern zu verarbeiten. Der Unterschied zwischen KI und Chatkontrolle ist am Ende nur was man mit diesen Daten machen will.
Die Chatkontrolle ist zum Glück vorest gescheitert, aber die Tech-Konzerne bauen zur Zeit Infrastruktur die sich leicht anpassen lassen kann um diese umzusetzen. Recall kann die Screenshots nebenbei auch auf potenziell illegale Inhalte untersuchen und melden. Bei Gemini werden die Daten direkt von Google verarbeitet und neben der AI kann auch noch ein zweites System Analysen durchführen.
Die so entstehenden Daten-Silos werden auch ein begehrtes Ziel für die Sicherheitsbehörden werden. Da die Infrastruktur beim nächsten Versuch eine Überwachung einzuführen quasi schon existiert, wird die Abwehr diese Begehren deutlich schwieriger werden.
You don't need to parse the data from the Wikipedia page, it is also available in Wikidata. However the data is not (always?) the same as in Wikipedia. For the example below the latest album was not linked yet (fixed now).