1/26/2016

HTML5 Tutorial: Canvas Pixel Manipulation

In diesem Tutorial wird beschrieben, wie ihr mit Hilfe vom ImageData Objekt Zugriff auf die Pixel des HTML5 Canvas Elements bekommt. 

Zuerst schauen wir uns die Grundlagen an bevor wir tiefer in die Materie eintauchen.
Voraussetzung für dieses Tutorial ist, dass ihr bereits mit dem Canvas Element vertraut seid und wisst wie ihr damit umzugehen habt. Und natürlich solltet ihr zumindest grundliegende Kenntnisse in JavaScript haben. 

Das Thema „Pixel Manipulation“ ist die Grundlage so ziemlich von allen Experimenten die ich in letzter Zeit hier auf meinem Blog veröffentlich habe. Hier eine Auswahl an Experimenten (Die meisten mit Partikeln in 3D, dazu wird es ein weiteres Tutorial geben):


Los geht’s.

Was ist das ImageData Objekt,
wie erstellen wir eins und wie kommen wir an die Pixelinformationen?

Das ImageData Objekt hat 3 Eigenschaften (Read only):

ImageData.width
ImageData.height
ImageData.data

Die beiden Eigenschaften width und height beinhalten die Werte für die Breite und Höhe.
Die data Eigenschaft beinhaltet die Pixelinformationen an die wir gelangen wollen und mit deren Hilfe wir Zugriff auf die Pixel bekommen. ImageData.data ist im Prinzip nichts anderes als ein Array. Genauer gesagt ein Uint8ClampedArray das die Pixelinformationen in der Reihenfolge RGBA beinhaltet mit Werten von 0 – 255. Später mehr dazu.

Zunächst einmal erstellen wir ein ImageData Objekt:

var canvas = document.createElement( 'canvas' );
    canvas.width = 100;
    canvas.height = 100;

document.body.appendChild( canvas );

var context = canvas.getContext( '2d' );

var imageData = context.createImageData( 100, 100 );

Wir haben nun mit Hilfe der Methode createImageData() ein leeres ImageData Objekt erstellt mit einer Breite und Höhe von 100 Pixeln. Alle Pixel in diesem neuen Objekt sind per Voreinstellung auf schwarz mit einer Transparenz von 0 gesetzt. Oder anders ausgedrückt, jedes Pixel hat folgende Werte:

R = 0;//rot
G = 0;//grün
B = 0;//blau
A = 0;//alpha

Neben der Methode createImageData() gibt es noch eine weitere Möglichkeit ein ImageData Objekt zu erstellen. Ihr könnt mit Hilfe der Methode getImageData() auch die Pixelinformationen aus einer Canvas auslesen.
Im folgenden Beispiel erstellen wir wieder eine Canvas. Aber im Gegensatz zum vorherigen Beispiel färben wir dieses Mal die Canvas per fillRect rot ein. Dementsprechend sind die RGBA Pixelinformationen die wir erhalten anders als im ersten Beispiel.

var canvas = document.createElement( 'canvas' );
    canvas.width = 100;
    canvas.height = 100;

document.body.appendChild( canvas );

var context = canvas.getContext( '2d' );
    context.fillStyle = '#FF0000';
    context.fillRect( 0, 0, 100, 100 );

var imageData = context.getImageData( 0, 0, 100, 100 );

Beispiel.

Nun haben wir ein ImageData Objekt erstellt das die Pixelinformationen der Canvas enthält.
Da wir unsere Canvas rot eingefärbt haben besitzen die Pixel nun folgende Werte:

R = 255;//rot
G = 0;//grün
B = 0;//blau
A = 255;//alpha

Wir haben also zwei Methoden zur Auswahl mit denen wir ein ImageData Objekt erstellen können.
Die Methode createImageData() eignet sich vor allem dann, wenn man ein völlig neues und leeres „Bild“ als Grundlage haben möchte. Mit der Methode getImageData() hingegen kann man z.B. die Pixel eines geladenen Bildes auslesen und verändern.

Schauen wir uns nun die Werte genauer an die wir aus dem data Array auslesen können. Zunächst einmal die Grundlagen zu RGBA:

R = rot (ein Wert von 0 – 255)
G = grün (ein Wert von 0 – 255)
B = blau (ein Wert von 0 – 255)
A = alpha (ein Wert von 0 – 255. 0 ist transparent und 255 voll sichtbar)

Im folgenden Beispiel lesen wir die Daten für das erste Pixel aus unserem per getImageData() erstellten imageData Objekt aus:

var r = imageData.data[ 0 ];
var g = imageData.data[ 1 ];
var b = imageData.data[ 2 ];
var a = imageData.data[ 3 ];

Wir wissen nun die Farbe des ersten Pixels (links oben) und können dem Beispiel entnehmen, dass die ersten vier Werte aus dem data Array, nämlich 0, 1, 2 und 3 ein Pixel repräsentieren. Folgerichtig besteht jedes Pixel aus vier Werten (r, g, b, a) und das hilft uns dabei wenn wir über das data Array iterieren wollen um an alle Pixelinformationen zu gelangen.
Um über das data Array zu iterieren nehmen wir eine übliche for-Schleife:

for ( var i = 0, i < imageData.data.length; i += 4 ) {

    var r = imageData.data[ i ];
    var g = imageData.data[ i + 1 ];
    var b = imageData.data[ i + 2 ];
    var a = imageData.data[ i + 3 ];

}

Auf diese Weise erhalten wir die RGBA Farbinformationen eines jeden im data Array gespeicherten Pixels. Der Trick hierbei besteht darin den Schlüssel i nicht um den Wert 1 zu erhöhen (i++), sondern um 4 (i += 4).
Denn, wie wir bereits wissen, besteht jedes Pixel aus 4 Werten (r, g, b, a) und somit kommen wir in jedem Schritt der for-Schleife an die Farbinformationen eines Pixels.
Hier gibt es noch ein sehr gutes Tutorial genau zu dem Thema.


Um das iterieren über das Array zu optimieren empfehle ich unbedingt 2 Änderungen an dem obigen Beispiel vorzunehmen. Zum einen speichern wir imgData.data in einer extra Variablen und die for-Schleife schreiben wir wie folgt auch etwas anders.

var data = imageData.data;

for ( var i = 0, l = data.length; i < l; i += 4 ) {

    var r = data[ i ];
    var g = data[ i + 1 ];
    var b = data[ i + 2 ];
    var a = data[ i + 3 ];

}

Auf diese Weise läuft das Auslesen der Pixelinformationen performanter ab. In den folgenden Beispielen setze ich voraus das data als extra Variable gespeichert ist.
Hier noch ein schöner Link mit weiteren Tipps zur Optimierung.


Auslesen ist das eine,
aber nun gehen wir einen Schritt weiter und verändern die Farbinformationen.

Im folgenden Beispiel geben wir allen Pixeln aus unserem data Array die RGBA Werte für die Farbe Grün.

Grün sieht so aus:

R = 0; 
G = 255; 
B = 0; 
A = 255;

Und nun ändern wir die Werte, die zuvor noch rot waren entsprechend in grün um:

for ( var i = 0, l = data.length; i < l; i += 4 ) {

    data[ i ]     = 0;
    data[ i + 1 ] = 255;
    data[ i + 2 ] = 0;
    data[ i + 3 ] = 255;

}

Wir haben nun allen Pixeln die im data Array enthalten sind neue Werte zugewiesen.
Doch wieso wirkt sich diese Änderung nicht aus? Unsere Canvas ist immer noch rot und nicht grün.
Damit sich unsere „Pixel Manipulation“ auswirkt müssen wir noch einen Schritt durchführen:

context.putImageData( imageData, 0, 0 );

Mit der Methode putImageData() kopieren wir die von uns abgeänderten Pixelinformationen des ImageData Objekts zurück in die Canvas an die Position x = 0 und y = 0.
Und nun ist unsere Canvas nicht mehr rot, sondern grün.

Hier nun zusammengefasst das komplette Beispiel:

var canvas = document.createElement( 'canvas' );
    canvas.width = 100;
    canvas.height = 100;
 
document.body.appendChild( canvas );

var context = canvas.getContext( '2d' );
    context.fillStyle = '#FF0000';
    context.fillRect( 0, 0, 100, 100 );

var imageData = context.getImageData( 0, 0, 100, 100 );
var data = imageData.data;

for ( var i = 0, l = data.length; i < l; i += 4 ) {

    data[ i ]     = 0;
    data[ i + 1 ] = 255;
    data[ i + 2 ] = 0;
    data[ i + 3 ] = 255;

}

context.putImageData( imageData, 0, 0 );

Beispiel in JSFiddle.

Wir verfügen nun über die Grundlagen und wissen wie wir Pixelinformationen auslesen und verändern können.
Natürlich macht es wenig Sinn eine komplette Fläche, also eine komplette Canvas auf diese Weise von einer Farbe in eine andere umzufärben. Dafür gibt es auch andere Methoden für die wir nicht einmal Zugriff auf die Pixel benötigen. Um aber beispielhaft zu erklären wie das ganze grundliegend funktioniert ist es dann eben doch sinnvoll.

Folgend ein etwas anderes Beispiel, welches man ohne den Zugriff auf die Pixel so nicht umsetzen könnte. Wir geben jetzt jedem einzelnen Pixel aus dem data Array per random einen zufälligen Farbwert:

var canvas = document.createElement( 'canvas' );
    canvas.width = 100;
    canvas.height = 100;
 
document.body.appendChild( canvas );

var context = canvas.getContext( '2d' );

var imageData = context.getImageData( 0, 0, 100, 100 );
var data = imageData.data;

for ( var i = 0, l = data.length; i < l; i += 4 ) {

    data[ i ]     = Math.floor( Math.random() * 255 );
    data[ i + 1 ] = Math.floor( Math.random() * 255 );
    data[ i + 2 ] = Math.floor( Math.random() * 255 );
    data[ i + 3 ] = 255;

}

context.putImageData( imageData, 0, 0 );

Beispiel in JSFiddle.
  
Und hier noch drei weitere JSFiddle Beispiele. Dieses Mal laden wir ein externes Bild und zeichnen dieses, nachdem es geladen worden ist, per drawImage in die Canvas und verändern dessen Pixelinformationen.

Beispiel 1: Farben invertieren
Beispiel 2: Ein Graustufenbild erzeugen
Beispiel 3: Per Schwellenwertverfahren Farbwerte zufällig segmentieren

Hier noch ein externes Tutorial zu diesem Thema, das etwas tiefer in die Materie Filter eintaucht und sehr schöne Beispiele beinhaltet.

Anhand dieser Beispiele sollte klar werden wie mächtig der Zugriff auf die Pixel sein kann.
Achtung: Wenn wir mit der Methode drawImage ein Bild in die Canvas zeichnen und Zugriff auf die Pixel haben wollen, dann muss sich dieses Bild auf dem gleichen Server befinden auf dem auch das JavaScript ausgeführt wird! Ist das nicht der Fall wird die Konsole eures Browsers eine Fehlermeldung ausgeben und der Zugriff auf die Pixel wird verweigert:


SecurityError: The operation is insecure.

Man kann das umgehen. Aber der Weg ist steinig und nicht in jedem Fall durchzuführen.
Das Stichwort hierbei lautet CORS. Vereinfacht ausgedrückt: Der Besitzer des Bildes muss euch den Zugriff auf das Bild erlauben. Wie man das macht wird hier beschrieben.

Wenn ihr dann ein solches Bild von einem anderen Server laden wollt, dann müsst ihr unbedingt die folgende Zeile in eurem Script ergänzen:


image.crossOrigin = "anonymous";

Wichtig hierbei. Diese extra Zeile muss vor der Zeile eingefügt werden in der ihr den src-Pfad des Bildes angebt. Hier ein Beispiel wie ihr ein solches Bild laden könnt:

var image = document.createElement( 'IMG' );
    image.onload = function () {
    
        //Image ready
        
    }
    image.crossOrigin = "anonymous";
    image.src = 'http://www.beispiel.de/seinBild.jpg';

Und hier noch einmal zum Vergleich ein Beispiel wie ihr ein Bild vom gleichen Server laden könnt:

var image = document.createElement( 'IMG' );
    image.onload = function () {
    
        //Image ready
        
    }
    image.src = 'deinBild.jpg';

Das aber nur kurz zur Erklärung. Nicht das ihr euch wundert, wenn ihr ein Bild von einem anderen/fremden Server ladet und dann die hier gelernten Inhalte nicht funktionieren.

Kommen wir nun aber zurück zum eigentlichen Inhalt dieses Tutorials. 

Bisher haben wir einfach immer nur alle Pixel eines Bildes ausgelesen und/oder verändert. Was aber wenn wir gezielt einzelne Pixel ansprechen/auslesen wollen?
Dazu brauchen wir zwei Funktionen/Methoden, die es uns erlauben anhand von XY-Koordinaten einzelne Pixel aus dem data Array anzusprechen.

Fangen wir damit an einzelne Pixel aus dem data Array per XY-Koordinaten zu verändern.
Das Problem. Wir bekommen per data Array nur über den Index Zugriff auf die einzelnen Werte.
Daher müssen wir einen kleinen Trick anwenden um unsere X und Y Werte in den Index umzurechnen. Eigentlich ganz einfach. Und so wird’s gemacht:

var i = ( x + y * imageData.width ) * 4;//i = index

Verpacken wir das jetzt in einer Funktion, die als Paramater X, Y und ein Objekt (c) mit den Farbwerten RGBA entgegen nimmt, kommt das dabei raus:

function setPixel( x, y, c ) {

    var i = ( x + y * imageData.width ) * 4;

    data[ i ] = c.r;
    data[ i + 1 ] = c.g;
    data[ i + 2 ] = c.b;
    data[ i + 3 ] = c.a;

};

Wir haben nun eine Funktion, die es uns erlaubt die Farbwerte eines per XY-Koordinaten ausgewählten Pixels zu verändern.
Dabei ist zu beachten, dass die X und Y Werte ganzzahlige Werte ohne Komma und Dezimalstellen sind. Schließlich gibt es keine Pixel auf Koordinaten mit Komma. Es gilt also, wenn vorhanden, Fließkommazahlen (Float) vorab in Integer umzuwandeln. Vereinfacht ausgedrückt: Bevor wir die Funktion setPixel aufrufen, müssen wir dafür sorgen, dass die XY-Koordinaten keine Kommas mehr enthalten. Es gibt mehrere Wege das zu tun. Ich zeige hier 2 Möglichkeiten auf, wobei ich immer die zweite Variante verwende.

Variante 1 mit Math.floor():

var x = 19.123425235;
var y = 45.534534213;
var c = { r:0, g:0, b:0, a:255 };

function setPixel( x, y, c ) {

    var i = ( x + y * imageData.width ) * 4;

    data[ i ] = c.r;
    data[ i + 1 ] = c.g;
    data[ i + 2 ] = c.b;
    data[ i + 3 ] = c.a;

};

setPixel( Math.floor( x ), Math.floor( y ), c );

Variante 2 mit Bitwise OR operator:

var x = 19.123425235;
var y = 45.534534213;
var c = { r:0, g:0, b:0, a:255 };

function setPixel( x, y, c ) {

    var i = ( x + y * imageData.width ) * 4;

    data[ i ] = c.r;
    data[ i + 1 ] = c.g;
    data[ i + 2 ] = c.b;
    data[ i + 3 ] = c.a;

};

setPixel( x | 0, y | 0, c );

In beiden Beispielen färben wir jeweils ein Pixel schwarz ein. Daher auch die Variable c, bzw. das Objekt c mit den Werten für RGBA:

var c = { r:0, g:0, b:0, a:255 };

Wie wir sehen beinhalten die Variablen für die XY-Koordinaten Fließkommazahlen. Einfach nur um das mit der Umwandlung zu verdeutlichen. Würden wir an dieser Stelle keine Umwandlung vornehmen, dann würden die hier gezeigten Beispiele versagen und die Funktion setPixel wäre nicht mehr in der Lage aus den XY-Koordinaten den Index für das Array zu berechnen.
Ich bevorzuge die Variante 2 mit dem Bitwise OR operator, weil das, wenn man sehr viele Pixel Zugriffe auf einmal hat, einfach ein Stück weit performanter läuft als mit Math.floor().

Und noch eine Kleinigkeit gilt es bei der Funktion setPixel zu beachten. Führt ihr Berechnungen durch, bei denen es vorkommen kann, dass die XY-Koordinaten außerhalb des Bereichs der Canvas liegen, dann müsst/solltet ihr durch eine zusätzliche If-Bedingung sicherstellen, dass diese nicht gezeichnet werden. Das führt sonst zu komischen, manchmal auch interessanten, aber bestimmt nicht gewollten Effekten. Hier ein Beispiel dafür, mit der Annahme, dass unsere Canvas eine Breite und Höhe von 100 Pixeln hat und das unser Pixel außerhalb davon liegt:

var x = -19.123425235;
var y = 450.53453213;
var c = { r:0, g:0, b:0, a:255 };

function setPixel( x, y, c ) {

    var i = ( x + y * imageData.width ) * 4;

    data[ i ] = c.r;
    data[ i + 1 ] = c.g;
    data[ i + 2 ] = c.b;
    data[ i + 3 ] = c.a;

};

if ( x > 0 && x <= 100 && y > 0 && y <= 100 )
 setPixel( x | 0, y | 0, c );

Hier noch einmal als Beispiel in JSFiddle mit Koordinaten innerhalb der Canvas.

Jetzt, wo wir wissen wie man die Farbwerte eines Pixels verändert, soll es nun darum gehen die Farbinformationen bestimmter Pixel auszulesen. Die folgende Funktion nimmt die Parameter X und Y entgegen und gibt ein Objekt zurück mit den Werten RGBA:

function getPixel( x, y ) {

    var i = ( x + y * imageData.width ) * 4;

    return { r:data[ i ],
             g:data[ i + 1 ],
             b:data[ i + 2 ],
             a:data[ i + 3 ] }

};

In diesem Beispiel in JSFiddle lesen wir per Zufall den Farbwert eines Pixels aus.

Mit den beiden Methoden setPixel und getPixel haben wir nun ganz komfortabel Zugriff auf die einzelnen Pixel der Canvas. Von hier an stehen uns im Prinzip alle Türen offen.

Folgend ein paar JSFiddle Beispiele mit denen wir per XY-Koordinaten die Pixel der Canvas verändern und somit Bilder aus Code generieren.


In einem weiteren Tutorial, das auf diesem aufbauen wird, werde ich dann erklären wie ihr die dritte Dimension und Partikel (wie hier bei diversen Beispielen schon gezeigt) auf eure Canvas bringt. 

Hier noch zum Abschluss eine kleine aber feine Auswahl an deutschsprachigen Tutorials rund um dieses Thema:


Ich hoffe das Tutorial hat euch gefallen. Falls noch irgendetwas unklar sein sollte hinterlasst einfach einen Kommentar oder schreibt mir. Ich würde mich freuen wenn ihr wieder vorbei schaut zu Teil 2.

1/25/2016

Sinus Landscape

Und wieder ein kleines Experiment.
Mit Hilfe der Sinus-, oder der Cosinusfunktion wird hier eine Art Landschaft erstellt. Über diverse Parameter lässt sich das Erscheinungsbild der Landschaft frei konfigurieren.
Per Mausklick könnt ihr zwischen verschiedenen Farbeschemen switchen. Die Rotation der Landschaft ist, wie bei den älteren Experimenten hier, auch wieder abhängig von der Mausposition. Aber nicht nur die Rotation, sondern auch die Position der Landschaft selbst ist abhängig von der Mausposition.
Für dieses Experiment wieder mit im Programm, das z-Sorting. Je nach Ausgabegerät solltet ihr das z-Sorting aber nicht bei mehr als 5000 Partikeln aktivieren.  Das Sortieren kostet einfach noch immer sehr viel Performance. Vielleicht finde ich hier noch eine bessere Lösung, aber bis dahin muss es so gehen. Falls jemand Ideen oder Vorschläge zu dem Thema hat gerne hier in den Kommentaren schreiben. Weiter unten gibt´s die Function mit der ich das z-Sorting  erledige.
Schaut euch das Experiment direkt hier an, oder, speziell wenn ihr mit einem mobilen Gerät unterwegs seid, folgt bitte diesem Link hier. Viel Spaß.



Und hier noch die Function die für das z-Sorting verantwortlich ist. Das Array particleHolder beinhaltet alle Partikel die es darzustellen gilt, bzw. enthält die Partikel die sortiert werden müssen:
model.particleHolder = model.particleHolder.sort( function( a, b ) {

    return ( b.vectorPosition.z - a.vectorPosition.z );

} );

1/18/2016

Pumping Torus Knot

Bei diesem Experiment wird ein Torusknoten abgebildet der Flüssigkeit durch sich durch pumpt. Die Geometrie dazu habe ich größtenteils von three.js übernommen, genauer gesagt aus der TorusKnotGeometry.js. Ich musste nur ein paar Parts umschreiben und habe dann noch das Pumpen ergänzt, welches ich mit einer simplen Sinus Kurve realisiert habe. Hier gibt es eine schöne Erklärung zum Thema Torusknoten in der auch die Gleichung dahinter erklärt wird.
Beim Experiment gibt es eine große Menge an möglichen Einstellungen. Ich will dieses Mal nicht zu sehr ins Detail gehen, bzw. die meisten Einstellungen sollten selbsterklärend sein. Probiert einfach mal ein paar Einstellungen aus und spielt an den Reglern.
Ich habe jetzt auch endlich Nutzer mit mobilen Endgeräten berücksichtigt. Neben einer verbesserten Darstellung und besonderen Einstellungen kann das Experiment nun auch über Touch Events gesteuert werden. Ben Centra hatte dazu dieses lesenswerte Tutorial geschrieben: Using Touch Events with the HTML5 Canvas.
Schaut euch das Experiment direkt hier an, oder, speziell wenn ihr mit einem mobilen Gerät unterwegs seid, folgt bitte diesem Link hier. Viel Spaß.


 

1/13/2016

3D Fireworks

Bei diesem JavaScript Canvas Experiment hab ich mich von einem alten Flash Experiment von mir selbst inspirieren lassen, welches ich damals für den Japantag in Düsseldorf geschrieben hatte:
Fireworks, Japantag in Düsseldorf am Rheintum
Das war 2009 und Flash war noch schwer angesagt. Bei dem alten Experiment musste ich noch auf die dritte Dimension verzichten, weil das die Performance kaum hergab. Und jetzt, mit JavaScript, kann man ein solches Feuerwerk fast problemlos abfeuern.
Ich bin jedes Mal wieder aufs Neue verblüfft wie sich JavaScript in den letzten Jahren weiterentwickelt hat.
Aber nun zum Experiment. Wie bei den vorhergegangenen Experimenten dieser Art, so nutzt auch dieses wieder Partikel für die Darstellung, die in Form von Pixeln direkt in das ImageData Array der Canvas geschrieben werden. Mit dieser Technik ist es möglich eine sehr große Anzahl an Partikeln gleichzeitig zu animieren und darzustellen. Und dank des Canvas Elements kann so ziemlich jeder Browser (außer dem Internet Explorer 9 und älter) das auch wiedergeben.
Zum Starten müsst ihr nur einmal kurz mit der Maus über die Fläche weiter unten fahren und schon geht’s los. Wie immer könnt ihr über „Open Controls“ diverse Einstellungen vornehmen und so direkt Einfluss auf das Feuerwerk nehmen. 


Folgend ein paar Anmerkungen zu den wichtigsten Einstellungen die direkt Auswirkungen auf das Feuerwerk haben:
Mit „Ground“ könnt ihr die Bodenplatte bzw. das Grid unten aus- und einblenden.
„Distance“ beschreibt die minimale Höhe in der die Feuerwerksraketen über der Bodenplatte explodieren.
Per „Interval“ bestimmt ihr die Häufigkeit mit der neue Raketen abgefeuert werden. Die Einheit hierbei sind Millisekunden. Und mit „Probability“ bestimmt ihr die Wahrscheinlichkeit mit der per Intervall neue Raketen in den Himmel entlassen werden. Wobei der Wert „1“ 100% entspricht. Bei „1“, also 100% werden alle 25 Millisekunden Raketen abgeschossen und bei „0.25“ nur mit einer Wahrscheinlichkeit von 25%.
Mit „Duration“ könnt ihr festlegen wie lange eine Explosion dauert.
Und per „Trail Length“ legt ihr fest ob die einzelnen Partikel eine Spur hinter sich herziehen sollen und wie lang diese ist.
Ich habe mich dieses Mal gegen die Option z-Sorting aktivieren zu können entschieden, weil ich das hierbei für nicht so wichtig fand, und weil das natürlich auch immer sehr viel Performance kostet. 


Besucher mit Mobile Devices, oder falls ihr das Experiment lieber in einem neuen Browserfenster starten wollt klickt einfach hier. Ansonsten geht’s direkt hier drunter los. Viel Spaß.

1/12/2016

Hier kommt die Sonne

Und wieder ein JavaScript Canvas Experiment.
Dieses Mal hab ich versucht einen Stern, bzw. unsere Sonne mit Hilfe von Partikeln nachzubilden.
Die Partikel werden per Zufall in einem vorher definierten Radius auf einer Sphere platziert. Klickt ihr mit der Maus in die Anwendung startet ihr die Animation und die Partikel bewegen sich vom Zentrum hin weg nach außen in den Raum.
Mit etwas Phantasie kann man die Photosphäre erahnen und natürlich, so hoffe ich jedenfalls, das von der Sonne ausgehende Licht und die Sonnenkorona.
Mit bis zu 5.000.000 (200.000 * 25) Partikeln kann man mit Sicherheit auch den schnellsten Rechner in die Knie zwingen. Besonders „Particle Amount“, „Follow Length“ und vor allem „Z-Sorting“ können euren Rechner schnell an seine Grenzen bringen. Probiert mal ein paar Einstellungen aus und verändert die Farben und das komplette Aussehen/Verhalten der Partikel.
Hier geht es direkt zum Experiment. Viel Spaß.




Für Interessierte hier noch die Funktion mit der ich die Partikel per Zufall auf der Sphere verteile. Die Funktion „getRandomSpherePos“ nimmt zwei Parameter entgegen. Mit „center“ bestimmt man wo das Zentrum der Sphere liegt und mit „radius“ gibt man den Radius der Sphere an. Die Funktion gibt dann ein Objekt mit den Werten x, y und z zurück.
function getRandomSpherePos( center, radius ) {

    var u = Math.random();
    var v = Math.random();
    var theta = 2 * Math.PI * u;
    var phi = Math.acos( 2 * v - 1 );

    var x = center.x + ( radius * Math.sin( phi ) * Math.cos( theta ) );
    var y = center.y + ( radius * Math.sin( phi ) * Math.sin( theta ) );
    var z = center.z + ( radius * Math.cos( phi ) );

    return { x:x, y:y, z:z };

};
Folgend ein beispielhafter Aufruf der Funktion um eine einzelne Position zurückzubekommen:
var center = { x:0, y:0, z:0 };
var position = getRandomSpherePos( center, 150 );

console.log( position );
Und hier auch noch einmal auf JSFiddle.

1/06/2016

Thresholding Image Crossfader jQuery Plugin

Wie angekündigt folgt nun das jQuery Plugin zum Thresholding Crossfader Experiment.
Der Name des Plugins ist etwas sperrig geraten: „ThresholdingImageCrossfader“.  Aber dafür beschreibt er ganz gut was das Plugin macht.
Mit Hilfe des Plugins könnt ihr eine beliebige Anzahl an Bildern mit dem Thresholding-Effekt ineinander überblenden lassen. Es handelt sich also um eine kleine Bildergalerie wenn man es genau nimmt. Die Überblendung der Bilder findet in der Regel automatisch statt. Es sei denn der User möchte das nicht.
Per Parameter könnt ihr festlegen welche und wie viel Bilder in der Bildergalerie angezeigt werden sollen. Ihr könnt auch bestimmen wie lange ein Bild angezeigt wird bevor das nächste erscheint und wie schnell der Effekt beim überblenden abläuft. Zusätzlich könnt ihr die Richtung bestimmen mit der der Effekt abläuft. Natürlich könnt ihr die Größe per width und height festlegen und auch die Richtung in der die Bilder angezeigt werden kann eingestellt werden. 
Ein kleines Tutorial wie man das Plugin mit oder auch ohne jQuery nutzen kann und welche Features es gibt folgt weiter unten. Auch den Link zum Download der gepackten (2KB) „jquery.thresholdingimagecrossfader.min.zip“ findet ihr am Ende dieses Posts.



Die hier zum veranschaulichen eingesetzten Bilder stammen übrigens von der sehr schönen Seite „New Old Stock“.

Und so geht´s. 
Hier ein ganz simples Beispiel wie ihr das Plugin einsetzen könnt.
Zunächst einmal müsst ihr, wenn ihr mit jQuery arbeiten wollt die Imports haben:



Den folgenden Code packt ihr einfach in ein Script Tag rein:
$( document ).ready( function() {

    var settings = {

        images: [ './img/Img001.jpg', './img/Img002.jpg', './img/Img003.jpg' ],
        width: 480,
        height: 480

    };

    $( '#holder' ).thresholdingImageCrossfader( settings );

} );
Damit hab ihr schon eure erste kleine Bildergalerie erstellt. Wie ihr sehen könnt braucht es dazu nur noch ein Div. Ich hab meinem die id id='holder' gegeben, aber da seid ihr natürlich völlig frei.
Ihr könnt euch auch einfach bei diesem Beispiel den Quellcode anschauen, dann bekommt ihr einen Eindruck was zu tun ist.
Wie gesagt. Das Ganze funktioniert auch komplett ohne jQuery. Hier das Beispiel von oben ohne jQuery:

document.addEventListener( 'DOMContentLoaded', function( event ) { 

    var settings = {

        images: [ './img/Img001.jpg', './img/Img002.jpg', './img/Img003.jpg' ],
        width: 480,
        height: 480

    };

    var thresholdingImageCrossfader = new ThresholdingImageCrossfader( document.getElementById( 'holder'  ), settings );

} );
Das ist die minimale Startkonfiguration mit der ihr loslegen könnt.

Kommen wir nun zu den zusätzlichen Parametern die ihr mit Hilfe der „settings“ noch einstellen könnt. Folgend ein Beispiel mit allen möglichen Parameter und der dazugehörigen Erklärung:

var settings = {

    images: [ './img/Img001.jpg', './img/Img002.jpg', './img/Img003.jpg' ],
    width: 480,
    height: 480,
    delay: 3,
    step: 3,
    dir: 1,
    effectDir: 1,
    bgColor: '#000000',
    auto: true

};

$( '#holder' ).thresholdingImageCrossfader( settings );
Die ersten drei Parameter sind ja schon bekannt. Einmal das „images“ Array mit dem ihr angeben müsst welche Bilder geladen und angezeigt werden sollen. Gefolgt von „width“ und „height“ um die Breite und Höhe zu bestimmen.
Mit „delay“ legt ihr fest für wieviel Sekunden ein Bild zu sehen sein soll, bevor das nächste erscheint.

Per „step“ bestimmt ihr wie schnell der Effekt der Überblendung stattfinden soll. Erlaubt sind hier Werte von 1 - 255. Wobei eigentlich nur Werte zwischen 1 -10 wirklich Sinn machen. 1 ist dabei besonders langsam und folgerichtig 10 sehr schnell. Da ein Pixel nur einen Farbwert von 0 - 255 haben kann, ergibt sich daraus das Limit für diesen Parameter von 1 - 255. Würde mann an dieser Stelle 0 angeben, dann würde einfach gar nichts passieren.
Der Parameter „dir“ bestimmt in welcher Richtung die Bilder aus dem Array angezeigt werden sollen. Erlaubt sind hier die beiden Werte 1 und -1. Beim Wert 1 wird das Array von vorne bis zum Schluss abgearbeitet und bei -1 umgekehrt.
Mit „effectDir“ gebt ihr an in welcher Richtung der Effekt beim überblenden ablaufen soll. Auch hier sind die Werte 1 und -1 erlaubt. Bei einem Wert von 1 wird von 255 auf 0 heruntergeganen und bei einem Wert von -1 von 0 auf 255 hochgezählt. Beides sieht eigentlich sehr schick aus. Es kommt auch ein bisschen auf die Bilder an die ihr einsetzt was hier besser geeignet ist. Sind die Bilder sehr hell, oder sehr dunkel? Probiert es einfach mal aus.
Und mit „bgColor“ legt ihr einfach nur die Hintergrundfarbe fest die angezeigt wird bevor das erste Bild vollständig eingeblendet worden ist.
Der Parameter
„auto“ nimmt true oder false entgegen. Standardmäßig ist true eingestellt, was bedeutet, dass die Bilder automatisch hintereinander in einer unendlichen Dauerschleife angezeigt werden. Steht der Parameter auf false wird nur das erste, oder letzte Bild eingeblendet und nichts weiter passiert.

Zusätzlich dazu könnt ihr das Ganze auch steuern. Hier zwei Beispiele wie ihr per Klick zum nächsten, bzw. zum vorherigen Bild springen könnt:

$( '#holder' ).css( 'cursor', 'pointer' ).click( function() {

    $( '#holder' ).thresholdingImageCrossfader( { nextImage: null } );

} );
$( '#holder' ).css( 'cursor', 'pointer' ).click( function() {

    $( '#holder' ).thresholdingImageCrossfader( { previousImage: null } );

} );

Und hier nochmal das Gleiche vereinfacht ohne Click und ohne jQuery:
thresholdingImageCrossfader.nextImage();
thresholdingImageCrossfader.previousImage();

Das ist erst mein zweites jQuery Plugin. Es kann also gut sein das noch nicht alles optimal ist. Speziell der Aufruf der beiden Methoden „nextImage“ und „previousImage“ mit jQuery gefällt mir noch nicht wirklich. Wenn also jemand Verbesserungsvorschläge hat immer gerne her damit.

So. Und hier nun wie versprochen der Download Link zu der minifizierten JavaScript Datei.
Und hier gibt es zusätzlich auch noch die nicht minifizierte Version.


Unterstützte Browser: Firefox 3.0+, Safari 4.0+, Chrome 7+, IE9+, Opera 9+
Bzw. alle Browser die mit dem Canvas Element umgehen können.

1/04/2016

Sound Visualizer

Frohes Neues!
Zum Start ins neue Jahr präsentiere ich einen kleinen Sound Visualizer.
Natürlich, wie sollte es im Moment auch anders sein, mit Partikeln in einer dreidimensionalen Umgebung. Ihr könnt also mit der Maus die Szenerie drehen und per Mausklick zwischen vielen verschiedenen Konfigurationen wechseln. Es stehen 32 verschiedene Farbkombinationen zur Verfügung und dazu noch 4 unterschiedliche Anordnungen der Partikel.

Wie üblich könnt ihr über „Open Controls“ verschiedene Einstellungen vornehmen. Auf ein paar möchte ich hier kurz eingehen.
Mit „Particles Amount“ bestimmt ihr die Anzahl an Partikeln die zum visualisieren eingesetzt werden sollen. Wenn ihr einen schnellen Rechner habt, dann können das bis zu 100000 sein!  Mit „Particle Distance“ bestimmt ihr den Abstand der Partikel zueinander.
Mit „Frequency Scale“ legt ihr fest, wie stark die Particles auf die Frequenz reagieren und per „Buffer Length %“ bestimmt ihr wie viel Prozent vom Buffer für die Visualisierung benutzt werden soll.
Mit „Speed“ und „Smoothing Time“ nehmt ihr Einfluss wie empfindlich die Particles reagieren und mit welcher Geschwindigkeit.
Per „Color“ lässt sich ein Farbkanal von 0 – 255 frei verändern und somit könnt ihr auch auf die Farbgestaltung selber Einfluss nehmen.
Mit „Base“ legt ihr fest ob zusätzlich zu jedem Particle auch immer noch ein extra Particle angezeigt werden soll. Dieses extra Particle visualisiert die Basis, bzw. zeigt euch an wo auf der Y-Achse der Nullpunkt eines jeden Particles ist.
Die anderen Einstellungen sollten schon aus den anderen Experimenten bekannt sein oder selbsterklärend. Am besten einfach rumspielen und austoben.

Die Technik die dahinter steckt und auf die ich später noch ganz kurz eingehe wird leider noch nicht von allen Browsern unterstützt. Speziell auf mobilen Geräten ist die Soundwiedergabe im Browser und das Analysieren der Frequenz noch nicht möglich. Daher richtet sich dieses Experiment in erster Linie an Desktop User. Browser Plugins werden aber nicht benötigt, da alles in HTML5 umgesetzt ist.

Hier geht es direkt zum Experiment. Nur falls ihr die volle Performance herausholen wollt.

   
Falls ihr das Experiment nicht selbst ausprobieren könnt, weil z.B. euer Browser ungeeignet ist und ihr nicht auf den aktuellen Firefox oder Chrome ausweichen könnt, dann könnt ihr euch hier ein kleines Video dazu anschauen:



Zur Technik wie man an die Audiofrequenz kommt folgend ein paar Code-Schnipsel.
An dieser Stelle initialisiere ich den Sound, bzw. den Analyser. Hier findet ihr ein kleines Tutorial dazu.


audio = new Audio();
audio.src = 'DeinSong.mp3';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;

audioContext = new ( window.AudioContext || window.webkitAudioContext )();

analyser = audioContext.createAnalyser();

audioSrc = audioContext.createMediaElementSource( audio ); 
audioSrc.connect( analyser );

analyser.connect( audioContext.destination );
analyser.smoothingTimeConstant = smoothingTimeConstant;//0.50;
analyser.fftSize = particleAmount * 2;

analyserBufferLength = analyser.frequencyBinCount;

In der „requestAnimFrame“ Function dann macht ihr folgendes:
var frequencySource = new Uint8Array( analyser.frequencyBinCount );

analyser.getByteFrequencyData( frequencySource );

Und in der For Schleife dann für jedes Particle oder Objekt eurer Wahl dann:
var frequency = frequencySource[ audioBufferIndex ];
var y = frequency / frequencyFactor;

Wobei y dann den Wert beinhaltet der euer Objekt beeinflusst.

Und, natürlich ganz wichtig. Das Lied welches ihr hier zu hören bekommt ist von Polar DUBstep und hat den Titel: Piazzolla (spring) dubtep remix