Effetto fuoco con canvas, una demo old school

fxIn questi giorni estivi un po’ più calmi del solito mi sono messo a leggere con calma gli articoli sul tag CANVAS redatti da Alessandro, non avevo ancora fatto alcun test con il tag e volevo rinfrescarmi un po’ le idee.

Poi, gira che ti rigira mi sono imbattuto nel simpaticissimo JS1k Javascript demo contest, se ne parla anche su Ajaxan, e volendo approfondire l’argomento CANVAS mi son detto, perchè non simulare uno degli effetti demo che tanto mi piacciono quanto sono inutili (se la sperimentazione può essere considerata inutile) ? E allora ecco che qualcosa è saltato fuori.

Prima di tutto per chi non lo sapesse, una demo (demoscene) è un programma dimostrativo, solitamente scritto in C++ (talvolta anche in linguaggio macchina), che in pochissimi kilobyte di codice (di solito 64k o 4k) deve sbalordire chi lo guarda creando effetti grafici realtime. E’ un ricettacolo di abilità e creatività del programmatore che si diletta a creare effetti di visualizzazione con il minor codice possibile. Ci sono interi siti ricchi di demo che fanno cose sbalorditive, dal più piccolo effetto in 2d ad intere scene in 3d.

Alcune di queste demo sono diventate un must, come ad esempio l’effetto plasma (qui una implementazione JS senza canvas) o l’effetto fuoco, visto le mie potenzialità di tamarro mi sono dilettato in quest’ultimo proprio utilizzando le spiegazioni dell’articolo linkato e riproducendo il codice da C++ a Javascript.

Il risultato lo potete vedete alla pagina d’esempio, FIRE con canvas (meglio visualizzato con Chrome, su Firefox per qualche strano motivo il disegno del testo a volte fa scattare una Exception di Javascript… )!

fire

Il risultato a livello di performance lascia un po’ a desiderare, soprattutto nella combinazione Firerox / Mootool. Nella prima versione del codice non ho utilizzato Mootools (ho semplicemente racchiuso tutto in una classe, il codice utilizza poco o niente del framework, forse due accessors ad attributi) e su Firefox le performance erano decisamente migliori, nessun cambiamento invece su Chrome.

Come funziona?

Il procedimento teorico è abbastanza semplice. Ad ogni frame dell’animazione, nel punto più basso del canvas (l’ultima riga) si creano una serie di pixel dal colore random. Dopodichè si elaborano tutti i pixel del canvas applicando un filtro a matrice (molto semplice, una specie di blur) ad ogni pixel e si vira il risultato ad una palette di colori predefinita (per avere l’effetto del colore del fuoco).

Il testo è stampato insieme alla riga di colore random per ogni frame con una intensità di colore variabile. Infine, ispirato dall’articolo di Alessandro riguardante le maschere, ho aggiunto un gradiente sopra il risultato che simula una maschera di sfumatura.

Devo dire che qualche difficoltà l’ho avuta. Un po’ perchè sono una capra poco specializzato con la matematica e sicuramente il codice sarà ottimizzabile, un po’ perché ho trovato vera difficoltà di performance (il browser si bloccava) nella manipolazione dei pixel.

La difficoltà più grossa rispetto alla demo originale l’ho trovata nella performance di ridisegno di ogni singolo pixel. Sono partito con l’utilizzo della proprietà ImageData del canvas che in poche parole ritorna uno snapshot dei pixel del canvas che possono essere manipolati come descritto nel pratico articolo di hacks.mozilla.com. La tecnica è risultata impossibile da utilizzare per un lavoro del genere. Ogni volta che una delle funzioni di put o get dei ImageData viene toccata, il browser si mette letteralmente a piangere.

Sicuramente non aiuta il fatto che per leggere il colore di un pixel, è necessario leggere (anziche un solo intero e convertirlo), tre elementi dell’array differenti in tre posizioni differenti e comunque convertirli. Questa è una scelta delle API di canvas che non riesco a capire e che a naso, ammazza le performance, ma forse è solo la mia impressione/ignoranza. Servirebbe l’opinione di un vero esperto.

Ho ovviato disegnando dei rettangoli al posto del singolo pixel. Ovviamente più il rettangolo è grosso, più è veloce l’effetto in quanto ce ne sono di meno da disegnare, ma più è grosso il rettangolo, meno è definito l’effetto. Qui sotto l’esempio, ricordiamoci che è solo un esperimento. Ogni improvement è bene accetto.

[iframe src=”//mootools.net/shell/FH9j9/1/embedded”]