Von Wordpress nach MODx - Ein Erfahrungsbericht, Teil 1: Warum wechseln?

Veröffentlicht am 26. Juli 2011

Das Design nervt, alle Inhalte außer dem Blog passen nicht mehr, die Technik im Hintergrund fällt auseinander und generell ist man nur noch genervt: es wird Zeit die eigene Webseite zu überarbeiten. Genau das ist seit Monaten mein Nebenher-Projekt Nummer 1 und so langsam ist die Zielgerade in Sicht. Diese Überarbeitung enthält einen CMS-Wechsel – nach einem halben Jahrzehnt auf Wordpress-Basis wird in Zukunft MODx Revolution Verwendung finden. Und da ich mehrfach um die Darlegung meiner Gründe für den Wechsel sowie einen Erfahrungsbereicht gebeten wurde, gibt es jetzt diesen Artikel (und alle die da ggf. noch folgen mögen).

Ziele der Überarbeitung

Bevor es aber um die CMS-Frage geht, sind dies die Gründe für die Überarbeitung an sich:

  1. Ich bin kein Webdesigner und Frontendentwickler mehr, sondern in erster Linie HTML5-Erklärbär. Das sollte die Seite eigentlich schon irgendwie wiederspiegeln, auch jenseits des Blogs.
  2. Design und Technik der Seite wie sie jetzt ist, sind zu kompliziert. Das hat teilweise historische Gründe und liegt zum Teil an Wordpress. Weniger bewegliche Teile, die kaputt gehen können, sind das Ziel.
  3. Hosting-Umzug. Ausgewachsene Männer haben sich nicht mit 10-Euro-PHP-Hosting von der Stange zufrieden zu geben, sondern haben eine flexiblere, selbstbestimmte Server-Umgebung verdient.
  4. Der angesprochene CMS-Wechsel.

Argumente gegen Wordpress

Warum aber nun das CMS wechseln? Als ich vor ziemlich genau 5 Jahren das systematische Bloggen begann, war Wordpress eine naheliegende, schlanke Lösung, die für meine Zwecke wie geschaffen war. Wordpess ist leicht zu installieren und es ist eine große Leistung der Entwickler, dass man auch fast ohne Kenntnisse von PHP oder WP-Interna das System gut den eigenen Bedürfnissen anpassen kann. Außerdem habe ich damals mit Wordpress-Webwork so manchen Euro verdienen können. Seither hat sich so manches geändert:

  1. Mit der wachsenden Webtechnik-Fachkenntnis meiner Seite wurde der große Pluspunkt von Wordpress (absolute DAU-Kompatibiblität) zum Fluch. Mit dem Wissen um OOP und ORM macht der wuchernde Funktionsdschungel von Wordpress keinen Spaß mehr und auch die Docs verwirren mich zunehmend mehr, als dass sie mich erhellen. Zu viele Funktionen machen ungefähr das gleiche. Das ist gut, wenn man nur schnell zu irgendeinem Ergebnis kommen möchte, aber wenn man den Anspruch hat, es gut und richtig zu machen, ist es schwierig.
  2. Andererseits müssen viele elementare Funktionen wie z.B. Caching in Wordpress via Plugin nachgerüstet werden. Das erhöht den Anteil der beweglichen Teile im System und das kann ich nicht gebrauchen.
  3. Auch die Ergebnis einiger Wordpress Kern-Funktionen genügen meinen Ansprüchen nicht mehr. Ohne große Hackerei ist es nicht möglich elementare Grundprinzipien wie unobstrusive JavaScript einzuhalten? Das darf einfach nicht sein.
  4. Ich finde es unerträglich, wenn ein CMS versucht zu verhindern, dass der Entwickler die Ausgabe von HTML und Inhalt den eigenen Wünschen anpasst. Wordpress hat das früher nicht gemacht, jetzt schon. Ich habe keinen Anlass, mir das bieten zu lassen.
  5. Ich mache kein Wordpress-Webwork mehr. Mein einziges verbliebenes WP-Projekt war mein Blog und sobald das nicht mehr der Fall ist, kann ich einen ganzen CMS-Kosmos aus meinem Kopf und meinem Feedreader verbannen. Die gewonnene Zeit und Hirnkapazität lässt sich dann in andere Dinge stecken.

Warum MODx?

MODx ist als CMS recht flexibel. Es gibt einem nichts vor, sondern stellt nur die Bausteine zur Verfügung, um schnell zum Ziel zu kommen. Das macht mir zunächst einmal die Migration sehr leicht, denn da wo ich keine Lust auf Veränderungen habe (z.B. Permalinks und Seitenstruktur), lasse ich MODx einfach Wordpress emulieren. Hingegen erlaubt mir die Flexibilität, zukünftige Radikalumbauten leichter abzuwickeln. Und obwohl MODx weniger eingebaute Features hat als Wordpress, brauche ich weniger Plugins, um zum gleichen Funktionsumfang zu kommen. Dieses Wordpress-Blog hier verwendet im Moment 26 Plugins, das von MODx nur 8. Der Rest lässt sich durch Filter-Tags oder andere einzeilige Codeschnipsel umsetzen. Weniger bewegliche Teile! Das was MODx fehlt, fehlt mir nicht: auf Trackbacks und komplizierte Media-Verwaltung kann ich verzichten.

Andererseits ist MODx ein sehr mächtiges Content Management System, mit dem man auch mehrere Seiten auf einmal betreiben kann. Da ich mittlerweile auch mehr als eine Seite im Netz habe, bietet es sich an, alles in Zukunft unter einen Hut zu bringen. Weniger bewegliche Teile, alles zentral gesteuert.

Lohnt sich denn die Mühe? Ja, denn verglichen mit einem neuen Wordpress-Theme, bei dem man jedes Stück HTML den eigenen Wünschen anpasst, ist der Bau eines neuen MODx-Designs nebst Migration nicht wirklich mühsamer. Umfassende Blog-Funktionalität lässt sich MODx innerhalb von wenigen Minuten beibringen und auch das Übertragen der Daten ist mittels 200 Zeilen Migrations- und Legacy-Beseitungs-Script schnell gemacht. Wie genau das geht, erklärt dann gerne in folgenden Teilen … wenn denn Interesse besteht.

Die HTML5-Elemente <details> und <summary>

Veröffentlicht am 22. Juli 2011

Twitter- und G+-Gefolge bat mich, ein paar Zeilen über zwei neue HTML5-Elemente zu verlieren; also frisch ans Werk! Die Elemente <details> (Specs) und <summary> (Specs) gehören zu den wenigen HTML5-Neuheiten, mit denen sich auch ganz ohne JavaScript etwas spürbar neues in den Browser bringen lässt. Die Spezifikationen lassen uns wissen:

The details element represents a disclosure widget from which the user can obtain additional information or controls [...] The first summary element child of the element, if any, represents the summary or legend of the details. [...] The rest of the element's contents represents the additional information or controls.

Auf deutsch gesagt: der Inhalt eines <details>-Elements wird vor dem User so lange versteckt, bis der Surfer das im <details>-Element befindliche <summary>-Element anklickt oder anderweitig (z.B. via Tastaturnavigation) aktiviert. Als Inhalt gilt dabei alles bis auf das erste <summary>-Element – dieses bleibt immer sichtbar. Die beiden neuen Elemente bilden also zusammen ein interaktives Paket.

Nützlich kann dies zum Beispiel für das Verstecken von optionalen Interface-Elementen sein, die nicht ständig im Blickfeld des Nutzers sein müssen wie die Steuerung eines Videoplayers, Statusanzeigen oder Konfigurationsoptionen einer Webapp:

<video id="film" width="320" height="180" autoplay>
    <source src="video.mp4" type="video/mp4">
    <source src="video.webm" type="video/webm">
    <source src="video.ogv" type="video/ogg">
</video>

<details>
    <summary>Videosteuerung ein/ausblenden</summary>
    <p>
        <button id="start" onclick="start()">Start/Pause</button>
        <button id="stumm" onclick="stumm()">Stummschalten</button>
        <button id="lauter" onclick="lauter()">Lauter</button>
        <button id="leiser" onclick="leiser()">Leiser</button>
    </p>
</details>

In Browsern mit Unterstützung für <details> und <summary> (was Stand heute nur Chrome zu sein scheint) werden die Buttons erst sichtbar, nachdem man das <summary>-Element angeklickt hat:

Details und Summary für die Steuerung eines HTML5-Videoplayers

In Aktion (funktioniert nur in aktuellen Chrome-Versionen)

Wird kein <summary>-Element in dem <details>-Element gefunden, soll der Browser selbst eine <summary> mit einem Standardtext einfügen. Fügt man das open-Attribut ein (boolsches Attribut, in XHTML open="open", sonst einfach nur open), ist der Inhalt des <details>-Elements zu Beginn sichtbar. Chrome verhält sich auch bereits rundum standardkonform und implementiert sogar die korrekten User-Agent-Stylesheets.

In modernen Webapplikationen kann man die Funktionalität von <details> und <summary> sicher häufig gebrauchen, so dass man die Einführung der beiden neuen Elemente durchaus gutheißen kann – auch wenn man das Verhalten der beiden Neulinge mit wenigen Zeilen JavaScript nachbauen könnte. Die Unterscheid zwischen der <details>-<summary>-Kombination und einer JavaScript-Lösung sind im Einzelnen:

  • auch wenn die JavaScript-Lösung recht trivial wäre, sind <details> und <summary> noch einfacher einzubauen und über das open-Attribut zu steuern
  • <details> und <summary> funktionieren auch bei abgeschaltetem JavaScript
  • <details> und <summary> lasen sich, soweit ich das bisher beurteilen kann, nicht anpassen, Animationen und ähnliches sind nicht drin
  • Das <summary>-Element scheint zur Zeit noch nicht tastaturtauglich zu sein, weder lies es sich bei meinen Test antabben noch über einen Accesskey steuern

Angesichts der mangelnden Anpassbarkeit weiß ich nicht wirklich, ob sich <details> und <summary> außerhalb von Prototypen (wo die einfache Umsetzung von Vorteil ist) wirklich in naher Zukunft durchsetzen werden. Hinzu kommt die zur Zeit sehr sparsame Browserunterstützung. Zwar ließe sich ein JavaScript-Ersatz schnell programmieren, aber wenn man einen solchen Ersatz hat, der tastaturfreundlich und schick animiert ist, braucht man dann noch die Original-Elemente <details> und <summary>? Man wird sehen was zukünftige Entwicklungen mit sich bringen. Aber zumindest für Prototypen sind die beiden Elemente schon mal gut zu gebrauchen.

ECMAScript 5, die nächste Version von JavaScript – Teil 6: Function.prototype.bind

Veröffentlicht am 14. Juli 2011

Das letzte wirklich wichtige Feature von ES5, das wir noch nicht behandelt haben, ist die bind()-Methode von Function.prototype. Diese nützliche Funktion ist noch nicht in vielen Browsern nativ vorhanden, doch da der nötige Polyfill nur 20 Zeilen lang ist und ihn alle vernünftigen JavaScript-Bibliotheken (jQuery, MooTools und viele weitere) von Haus aus mitbringen, gibt es keinen Grund, bind() nicht einzusetzen.

Welches Problem löst bind()?

Innerhalb einer JavaScript-Funktion findet man immer (so gut wie immer – im Strict Mode nicht zwingend) ein Variable namens this vor, die das der Funktion zugehörige Objekt referenziert. Je nachdem wie man eine Funktion aufruft, ist das mal das eine, mal das andere Objekt:

var a = {
    foo: function(){
        console.log(this); // Hier ist "this" das Objekt "a"
    }
}

console.log(this); // Hier ist "this" das globale Objekt

function Make_B(){
    this.foo = function(){
        console.log(this); // Hier ist "this" das frisch erstellte Objekt "b"
    }
}
var b = new Make_B()
b.foo();

Wenn man eine Funktion via call() oder apply() aufruft, kann man bestimmen, was this sein soll und damit einige sehr clevere Dinge programmieren. Was aber, wenn wir bei der Erstellung der Funktion festlegen wollen, was this ist? Angenommen man wollte sich ein Widget programmieren, das bei einem Klick auf einen Button eine activate()-Funktion auslöst:

function Widget(){

    this.init = function(){
        var button = document.getElementById('clickme');
        button.onclick = function(){
            this.activate(); // Das geht schief - "this" ist hier der Button!
        }
    }

    this.activate = function(){
        alert('Widget aktiviert!')
    }

    this.init();

}

var my_widget = new Widget();

In diesem Fall referenziert this das Element, auf das wir den Event Listener setzen, also den Button. Das kann unter Umständen sinnvoll sein, hier wäre es uns aber lieber, wenn this unsere Widget-Instanz wäre. Für genau diese Fälle führt ES5 die bind()-Methode für Funktionen ein.

Was genau macht bind()?

Ruft man die bind()-Methode auf einer Funktion auf, wird eine neue Funktion zurückgegeben, die mit der Ursprungsfunktion so gut wie identisch ist, nur das this der neuen Funktion ist genau das Objekt, das man als erstes Argument an bind() übergeben hat. So lässt sich unser Widget-Problem einfach lösen:

function Widget(){

    this.init = function(){
        var button = document.getElementById('clickme');
        button.onclick = function(){
            this.activate(); // Jetzt geht es!
        }.bind(this);        // "this" der Funktion ist jetzt das "this" des Widgets!
    }

    this.activate = function(){
        alert('Widget aktiviert!')
    }

    this.init();

}

var my_widget = new Widget();

Dadurch, dass wir an den Eventhandler des Buttons bind(this) anhängen, wird das äußere this (das Widget) auch innen verwendet. bind() ist eine Methode von Function.prototype und steht damit (Browserunterstützung vorausgesetzt) allen Funktionen zur Verfügung.

Was kann bind() noch?

Zwar ist das Ändern von this der wohl häufigste Anwendungsfall von bind(), doch beim Erstellen der neuen Funktion kann auch die Liste der Argumente verändert werden. Jedes Argument von bind(), das auf das erste this-Argument folgt, wird als zusätzliches Argument vorne an die Argumentliste der Ursprungsfunktion angehängt:

// Gibt aus: ["A", "B", "C"]
var alt = function(){
    console.log(arguments);
}
alt("A", "B", "C");

// Gibt aus: ["foo", "bar", "A", "B", "C"]
var neu = alt.bind(null, "foo", "bar");
neu("A", "B", "C");

Das ist eine sehr nützliche Funktion für das Kapern von fremden Methoden. Wenn wir uns an die defineProperty()-Methode aus Teil 3 der Serie zurückerinnern, gab es dort das Problem, dass defineProperty() eine Methode des Object-Objekts und nicht von Object.prototype ist. Heißt also:

// Das hier hätten wir gern
meinObjekt.defineProperty('foo', bar);

// Aber das hier müssen wir schreiben
Object.defineProperty(meinObjekt, 'foo', bar);

Wie Molily in den Kommentaren von Teil 3 ganz richtig bemerkte, ist es aber mit nur einer Zeile Code möglich, mittels bind() ein defineProperty() für meinObjekt anzulegen:

meinObjekt.defineProperty = Object.defineProperty.bind(null, meinObjekt);

Das this ist in diesem Fall egal und daher null, aber dadurch, dass wir meinObjekt immer vor die Argumentliste von defineProperty() hängen, macht die Funktion auch als Methode von meinObjekt genau das, was sie soll.

Browserkompatibilität und Polyfills

An der Browserfront sieht es vergleichsweise mittelprächtig aus, denn nur die modernsten Surfprogramme (Firefox 4+, IE 9, Chrome) unterstützen bind() nativ. Aber es gibt auch zwei gute Nachrichten: erstens rüstet jede vernünftige JavaScript-Bibliothek (jQuery, MooTools, viele weitere) die Funktionalität von bind() nach und zweitens lässt sich selbst in Abwesenheit einer JS-Lib ein einfacher Polyfill schnell einbauen. Dieser ist zwar nicht völlig identisch mit der nativen Implementierung von bind(), was aber in 99,99% aller Fälle kein Problem sein dürfte.

Video-Manipulation mit Canvas Schritt für Schritt erklärt

Veröffentlicht am 30. Juni 2011

Da wir ja kürzlich im Zuge des in JavaScript geschriebenen MP3-Decoders die These „das muss doch auch mit Video gehen“ aufgestellt hatten, dachte ich, dass eine kleine Canvas-Video-Demo nebst Erklärungen angebracht wäre. Das was wir in diesem Artikel durchexerzieren ist nur das simpelste aller Beispiele, sollte aber für einen grundsätzlichen Überblick reichen; wir basteln uns ein kleines Script, das einen Videostream von einem Video-Element auf ein Canvas-Element kopiert und mit einen Hipster-Effekt versieht:

Der fertige Videoeffekt in Aktion

Grundlagen

Für unsere Effektmaschine brauchen wir HTML-seitig nichts weiter als ein Video-Element, zwei Canvas-Elemente und einen Script-Block:

<!doctype html>
<video id="film" width="320" height="180" controls>
    <source src="video.mp4" type="video/mp4">
    <source src="video.ogv" type="video/ogg">
</video>
<canvas id="zwischenablage" width="320" height="180" style="display:none"></canvas>
<canvas id="ziel" width="320" height="180"></canvas>
<script></script>

Warum zwei Canvas-Elemente? Unsere Video-Bearbeitung muss in zwei Schritten erfolgen, denn wir können nicht auf die einzelnen Pixel auf einem Video-Element zugreifen – das geht nur bei Canvas-Elementen. Also müssen wir unsere Video-Frames vom Canvas-Element auf Canvas 1 kopieren und von dort auf Canvas 2. Und weil Canvas 1 zu wirklich nichts weiter gebraucht wird, können wir sie auch mit display:none unsichtbar machen. Jetzt fehlt nur noch DOM-Zugriff auf die drei Elemente …

// Elemente in der Seite
var film           = document.getElementById('film'),
    ziel           = document.getElementById('ziel').getContext('2d');
    zwischenablage = document.getElementById('zwischenablage').getContext('2d');

… und schon kann es losgehen.

Die richtige Framerate abpassen

Das Ziel ist also nun, jedes Frame des Videos abzupassen, zu kopieren und zu modifizieren. Das ist schwieriger als man vielleicht zunächst meint, denn herkömmliche JavaScript-Timing-Mechanismen (setTimeout() und setInterval()) sind nicht besonders exakt; mit ihnen kann man nicht garantieren, dass man jedes Frame erwischt oder dass man nicht vielleicht unnötigerweise manche Frames zweimal kopiert. Die richtige Pfad führt über eine Funktion namens requestAnimationFrame() (Specs), die auf das nächste vom Browser gerenderte Frame wartet und dann einen Callback ausführt. Dieses Werkzeug sorgt, rekursiv angewendet, für einen präzisen Animations-Loop.

Der Haken an der Sache ist, dass requestAnimationFrame() eine sehr sehr neue Erfindung ist und in den diversen Browsern nur mit Vendor-Prefix zu finden ist – wenn überhaupt. Damit wir uns mit all diesen Varianten keinen Wolf programmieren setzen wir eine kleine Hilfsfunktion ein (adaptiert von Paul Irish) die uns eine verzweiflungsfreie API bereitstellt:

// Hack für requestAnimationFrame in allen Browsern
var animate = (function(){
    return window.requestAnimationFrame    ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame    ||
        window.oRequestAnimationFrame      ||
        window.msRequestAnimationFrame     ||
        function(callback){
            setTimeout(callback, 1000 / 60);
        };
})();

Hier wird dafür gesorgt, dass wir unter animate die bestmögliche Implementierung von requestAnimationFrame() vorfinden, die unser Browser zu bieten hat; zur Not auch eine Variante mit Vendor-Prefix oder wenn alle Stricke reißen auch ein herkömmlicher setTimeout().

Damit wir auch eine Animationsschleife erhalten, müssen wir animate wieder und wieder aufrufen, wobei jeder Aufruf der Funktion ein einzelnes Frame abhandelt. Auch diese Funktionalität stecken wir wieder in eine Hilfsfunktion:

// Die Kopier-Schleife
function loop(){
    if(!film.paused){
        animate(loop);
    }
}

Hier wird loop() immer wieder von sich selbst aufgerufen – jeweils durch unseren requestAnimationFrame() getunnelt und nur so lange, wie das Video auch läuft (film.paused nicht true ist). Fehlt nur noch der Startschuss für die Kopier-Schleife, wofür uns praktischerweise das Video-Element ein passendes Event bereitstellt:

// Die Kopier-Schleife starten
film.addEventListener('play', function(){
	loop();
}, false);

Zu diesem Zeitpunkt können wir unsere Animationsschleife starten, laufen lassen und stoppen, wobei freilich noch nicht wirklich etwas passiert – dazu müssen wir noch ein paar weitere Funktionen programmieren.

Pixel kopieren …

Auf ein Canvas-Element kann man mit der drawImage() ganz einfach fertige Grafiken zeichen – einfach neben den Ziel-Koordinaten ein HTML-Element angeben, das als Datenquelle dienen soll und fertig! Als Quelle können neben <img>-Elementen auch Canvas- oder Video-Elemente fungieren, so dass es ein leichtes ist, die Videoframes auf die Zwischenablage zu kopieren. Um an die Pixeldaten des Frames zu kopieren, müssen wir die Methode getImageData() auf der Zwischenablage bemühen. Diese gibt ein so geanntes ImageData-Objekt zurück, dass die Farbwerte jedes einzelnen Pixels enthält. Wenn wir dieses Objekt nun mit getImageData() auf die Ziel-Canvas schreiben …

// Diese Funktion kopiert Pixel von "film" auf "zwischenablage", dann auf "ziel"
function copy(){
    zwischenablage.drawImage(film, 0, 0);
    var bilddaten = zwischenablage.getImageData(0, 0, 320, 180);
    ziel.putImageData(bilddaten, 0, 0);
}

… und den Aufruf der copy()-Funktion in den Animationsloop einbauen …

// Die Kopier-Schleife
function loop(){
    if(!film.paused){
        copy(); // Frames kopieren
        animate(loop);
    }
}

… haben wie schon mal zumindest einen schönen sauberen Klon des Videos erzeugt. Wegen des Doppelschritts beim Kopieren haben wir durch das ImageData-Objekt Zugriff auf die Farbwerte jedes einzelnen Pixels in jedem Videoframe, so dass es uns ein leichtes ist, ein bisschen mit den Farben zu spielen.

… und verändern

Ein ImageData-Objekt enthält neben den Angaben width und height (geben die Maße des Ausschnitts an) ein Array, das der Reihe nach alle Farbwerte aller Pixel enthält. Der erste Eintrag im Array ist der Rot-Wert des ersten Pixels, der zweite Eintrag ist der Grün-Wert des ersten Pixels, an dritter Stelle folgt der Blau-Wert des ersten Pixels, dann kommt der Alpha-Wert des ersten Pixels und dann der Rot-Wert des zweiten Pixels … und so weiter. Diese Werte effektvoll zu manipulieren ist natürlich sehr einfach:

// Wendet den Effekt an
function effekt(bilddaten){
    var pixel = bilddaten.data;
    var i = 0;
    var r, g, b, new_r, new_g, new_b;
    while(i < pixel.length){
        // R, G und b holen...
        r = pixel[i],
        g = pixel[i + 1],
        b = pixel[i + 2];
        // Manipulieren...
        new_r = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189),
        new_g = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168),
        new_b = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
        // ... und speichern!
        pixel[i] = new_r;
        pixel[i + 1] = new_g;
        pixel[i + 2] = new_b;
        // Auf zum nächsten Pixel - den Alphawert einfach überspringen
        i += 4;
    }
    return bilddaten;
}

Die Funktion rattert einmal durch das Bilddaten-Array, spielt an den RGB-Werten der Pixel herum, überspringt den Alpha-Wert und gibt am Ende ein komplett überarbeitetes ImageData-Objekt zurück. Dieses müssen wir dann nur noch abbilden, d.h. wir müssen die Funktion effekt() in copy() einmal auf die Bilddaten anwenden

// Diese Funktion kopiert Pixel von "film" auf "zwischenablage", dann auf "ziel"
function copy(){
    zwischenablage.drawImage(film, 0, 0);
    var bilddaten = zwischenablage.getImageData(0, 0, 320, 180);
    bilddaten = effekt(bilddaten); // Effekt anwenden
    ziel.putImageData(bilddaten, 0, 0);
}

Und das war es dann auch schon! Das Ganze läuft halbwegs performant in jedem Browser, der Canvas- und Video-Element unterstützt, was heutzutage ja auf alles jenseits der Kreidezeit-Fraktion aus dem Hause Microsoft (IE 6 - 8) zutrifft.

Ausblick

Ein paar Pixel zu verdrehen ist also augenscheinlich nicht besonders schwer. Wollte man wirklich Videodaten erst im Browser generieren, wäre das aber im Prinzip auch kein Hexenwerk; mit der createImageData()-Methode des 2D-Kontext (Spezifikationen) kann man leere Bilddatensätze erstellen und diese dann mit Farbwerten für die diversen Pixel befüllen. Die Herausforderung liegt tatsächlich eher in der Codierung und Decodierung von Bilddaten, denn man will schließlich in Sachen Wiedergabe weder von dem löchrigen Codec-Support der Browser abhängig sein, noch möchte man darauf verzichten, bei Effektgeneratoren wie unserer kleinen Demo am Ende fertige Filme abzuspeichern. In diesem Bereich liegen die wahren Herausforderungen. Die Werkzeuge sind jedoch alle da. Es müsste nur mal jemand etwas daraus machen.