Safari on iPhone4S crashes with webkit-backface-visibility

I  removed flickering of an 2400px wide element while animating it by adding -webkit-backface-visibility:hidden; to the CSS. It worked fine. Later on a collegue reported that the Safari of his iPhone4S crashes everytime a section of the page is renderer. I managed to pinpoint the crash to the that line in the CSS. The fix was to replace it with -webkit-transform:translate3d(0,0,0);. No flickering or crashing after that!

Resetting scroll with jQuery freezes iOS

Listening to the scroll event with jQuery and resetting the scroll with window.scrollTo triggers a new scroll event on iOS. That of course resets the scroll again and trigger a new event and so on and so on… So don’t do this:

$(window).on("scroll",function(e){
e.preventDefault();
window.scrollTo(0,0);
});

Here’s a jsFiddle to demonstrate it:

http://jsfiddle.net/GbPF2/2/

Open the result window directly in your mobile:

http://fiddle.jshell.net/GbPF2/2/show/

A simple CSS should do the trick:


html,body{
 width:100%;
 height:100%;
 overflow:hidden;
}

But changing the orientation of the device eventually caused the content to scroll mysteriously, so I added a simple JS to prevent scrolling. It worked on all other platforms.

Detecting touch device with javascript

I’ve used Modernizr to detect browser features. It’s easy and awesome. Now I’ve been developing a site and testing it with Windows Phone 8 with Internet Explorer 10. The new IE does not support touch events, it supports pointer events. That’s why IE10 has ”no-touch” class in the HTML element.

To detect touch capability with IE10 support, use:

function isTouchDevice (){
//for wp8+ie10
if(navigator.msMaxTouchPoints) return true;
return $(”html”).hasClass(”touch”);
}

AS3: Perfect video sync with embedded frame numbers

Flash is still the best tool for online video and there are more and more gorgeous videos where external media like images from user’s Facebook profile are embedded to a video. World famous examples are Tackfilm, Take this lollipop and Lost in val Sinestra.

I participated in a similar campaign and our technical partner had a lot of problems with video sync. How can you tell with 100% reliability which video frame is currently shown? Zeh Fernando writes about possible solutions in this  article. The solutions (and their cons) are:

– embedding the video inside a SWF and getting the current frame of that movieclip (very inconvenient and cumbersome)
– use Netstream’s time property (very inaccurate)
– use NetStream’s decodedFrames + droppedFrames (works until Flash player hangs for one frame or the video is restarted or rewinded)
– check cue points (requires old video codec)

Eric Decker came up with a better solution. Ingenious!  Embed the the frame number to each frame with a barcode. Sounds very tedious, but quite easy task once the ExtendScript and ActionScript are ready. I didn’t find good examples, so I had to make one.

Frame numbers cannot be plain text, because decoding shapes would be too heavy for the CPU (nevermind the coder) and would not be 100% reliable. So the frame numbers are embedded as a binary number, ergo ones and zeros. My solution uses pixels of certain color, because it is very easy to get the color value of a pixel with ActionScript. If the color value exceeds a threshod value, the pixel equals ”1”.  First I tried a simple 1px high row of pixels below  the video, but compression causes pixels to blend with adjacent pixels so that is not even 70% reliable. So the blocks presenting binary value must be larger than 1*1px. In my tests even 3*3px blocks weren’t enough, if the compression is set to medium. 5×5 pixels with 7px gaps between blocks work with 100% reliability. I bet smaller gaps work too, but I didn’t test it.

Compression, and the blending caused by it, also fades the color, so the 100% color value (like 255,0,0 for pure red) cannot be used either. That’s why the AS3 script has a threshold parameter for the color value. If the parameter is 50, colors values decoded as ”1” would be 127 (255*50%) and above. You can easily test which threshold value is the best.

Here’s a screenshot of the video with some explanation:

Demo

The After Effects ExtendScript

I’ve never used After Effects before, but the script is so simple there probably isn’t a simpler way to add a barcode to a video. The script increases the height of the composition by certain amount (block height +bitOffsetFromBottom +bitOffsetFromTop) and adds a solid colored bar in that position. Then the number of frames is calculated from the frame rate and length of the video. That number is converted to binary (eg. 10010011) and one shape is added to the bar for each needed number (8 in this example).

The script adds an expression to each shape which controls the opacity of each shape as the playhead moves. Each shape is linked to a position in the binary number and if the number in that position is ”0”  the opacity is 0 and vice versa.

The AS3 script

After the AE script is run, it shows an alert box with used parameters and  those should be copied to Flash. This way the parameters between AE and AS3 match and you don’t have to set them by hand. Parameters are:

bitWidth: width of the shape
bitHeight: height of the shape
bitSpacing: : spacing between shapes
bgColor: background color
bitColor: shape color, this value is 0, 1 or 2.  0 indicates pure red, 1 pure green and 2 pure blue
bitOffsetFromBottom: shape distance from the bottom
bitOffsetFromTop: shape distance from the top of the background bar

AS3 has also the threshold parameter mentioned above.

It is also quite easy to test if the barcode works. The AS3 script warns if frames are skipped or the calculated frame number is smaller that previous, indicating that the barcode was calculated incorrectly. This is caused by too small shapes for the compression level and the solution is to increase the shape size and/or spacing.

One problem: the height of the video increases and we don’t want to show the binary shapes to the user. The AS3 script includes three solutions for this: mask the video, draw a shape over the barcode or capture each frame as a bitmap and cut off the barcode. Of course you can just make the height of the HTML element smaller and disable scaling.

Here is a demo. Note that I’ve added a text field to the video that displays the current frame as a binary. The AS3 script adds a textfield too that show the binary calculated from the shapes. This is just for debugging purposes and those can be disabled in AE and Flash.

Demo

 

Source files and downloads

Here’s the AE script:


{
// FrameNumberToBinary.jsx
	
    // copyright Niko Helle, nikohelle.net

    
    var bitWidth = 5;   // width of the shape representing binary
    var bitHeight = 8; // height of the shape representing binary
    var bitSpacing = 7; // spacing between shapes
    var bitBGHeight = 0; // do not set, calculated below
    var bitColor = [1,0,0]; /// color of the shape [R,G,B] Use only full color values: [1,0,0] or [0,1,0] or [0,0,1]. Other values will not work.
    var fillColor = [0,0,0]; /// color of the bar [R,G,B]
     var bitOffsetFromTop = 3; // shape distance from the top of the bar
    var bitOffsetFromBottom = 3;  // shape distance from the bottom of the bar
   
	
	function FrameNumbering(thisObj)
	{
        
        //calculate height of the bar
        bitBGHeight = bitOffsetFromTop+bitHeight+bitOffsetFromBottom; 
     
        //get the composition
        var firstComp = app.project.item(1);

        //increase the height by the bar height
        firstComp.height = firstComp.height+bitBGHeight; 

        // check if the there is enough space for the binary numbers. If shapes and spacing are wide, the numbers may not fit the video 
        if(!checkFrames(firstComp)){ 
            return;
        };

        //create the bar
       var bar = firstComp.layers.addSolid(fillColor,"bitBG", firstComp.width, bitBGHeight, 1) 
     
        // reset anchor for easier positioning
       resetAnchor(bar); 
       
       //position the bar to the bottom of the composition
       setPosition(bar,0,firstComp.height-bitBGHeight,false); 
   
        //create shapes
       calcBits(firstComp,bitColor); 
       
        // determine which color is used, and alert it for the AS3, 0=R, 1=G, 2=B. Only solid, full colors can be used!
        if(bitColor[0] == 1) bitColor = 0; 
        else if(bitColor[1] == 1) bitColor = 1;
        else bitColor = 2;       
       
       //alert used parameters so they can be copied to AS3
       
       var alertTxt = "Copy these values to the ActionScript file: \n";
       alertTxt +="_bitWidth="+bitWidth+";\n";
       alertTxt +="_bitHeight="+bitHeight+";\n";
       alertTxt +="_bitSpacing="+bitSpacing+";\n";
       alertTxt +="_bgHeight="+bitBGHeight+";\n";
       alertTxt +="_bitColor="+bitColor+";\n";
       alertTxt +="_bitOffsetFromBottom="+bitOffsetFromBottom+";\n";
       alertTxt +="_bitOffsetFromTop="+bitOffsetFromTop+";\n";
       alertTxt +="_frames="+(firstComp.frameRate*firstComp.duration)+";\n";
       
       alert(alertTxt); 
       
	}
	
    //sets position of an element
    function setPosition(element,x,y,z){ 

        var pos =element.transform.position.value;
        if(x !== false) pos[0] = x;
        if(y !== false) pos[1] = y;
        if(z !== false) pos[2] = z;
        element.transform.position.setValue(pos)

    }  

    // resets anchor to top left corner
    function resetAnchor(element){ 

        element.transform.anchorPoint.setValue([0,0,0])

    }  

    //calculate wether the binary fits the video
     function checkFrames(comp){ 
            
            var fps = comp.frameRate*comp.duration;
            var bitSpace = bitWidth*bitSpacing;
            var binaryLen = fps.toString(2).split("").length;
            bitSpace = bitSpace*binaryLen;
            if(bitSpace > comp.width) {
                alert("The video is not wide enough for the bit size and spacing. Required width is "+bitSpace+"px and video is "+comp.width+"px wide.");
                return false;
             }
            return true;

     }   
 
    //create shapes
    function calcBits(comp,bitColor){ 
        
        var fps = comp.frameRate*comp.duration;
         var bin = "1"+fps.toString(2);
         var bit;
         for(var i =0; i<bin.length;i++){
             createBit(comp,bitColor,i) 
         }   
     } 
 
    //create shape
    function createBit(comp,bitColor,pos){ 
         var gfx = comp.layers.addSolid(bitColor,"bit"+pos, bitWidth, bitHeight, 1)
          resetAnchor(gfx);
         setPosition(gfx,comp.width-(bitWidth+bitSpacing)*(pos)-bitWidth,comp.height-bitHeight-bitOffsetFromBottom,false);
         //add the expression
        var expression = 'bitpos = '+pos+';frame = timeToFrames();frame = parseInt(frame,10).toString(2).split("").reverse().join("");if(frame.length < bitpos) bit = "0";else bit = frame.substr(bitpos,1);if(bit == "1") value = 100;else value = 0;';
         gfx.opacity.expression = expression;
    }    

	//run the code
	FrameNumbering(this);
    

}

Here’s the  AS3:


/*
Capture current frame from video with a barcode
Copyright Niko Helle, nikohelle.net
*/

package 
{

	import com.nhe.video.VideoPlayer;
	import com.nhe.utils.Utils;
	import com.nhe.utils.ContentLoader;
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.text.TextField;
    import flash.text.TextFormat;  
    import flash.text.TextFieldAutoSize;  
	import flash.events.Event;
	import flash.geom.*;

	public class Main extends Sprite
	{


		// ignore all variables
		// set them in Main()
		// or copy them from AE's alert box after the ExtendScript is run
		
		private var _videoPlayer:VideoPlayer;
		private var _cl:ContentLoader;
		private var _blocker:Bitmap;
		private var _view:Bitmap;
		private var _bmd:BitmapData;
		private var _byteSlice:Bitmap;
		private var _tf:TextField;
		private var _lastBin:int = -1;
		private var _lastValues:Array = [];
		
		
		private var _threshold:uint = 50;
		private var _bgHeight:uint = 0;
		
		
		private var _frames = 0;
		private var _bitColor:uint = 0;
		private var _bitWidth = 0;
		private var _bitHeight = 0;
		private var _bitSpacing = 0;
		private var _bitOffsetFromBottom = 0;
		private var _bitOffsetFromTop = 0;
		
		
		
		
		public function Main() {
			
			/* Copy below parameters from the AE alert window after you have run the script*/			
			
			_bitWidth=5;
			_bitHeight=8;
			_bitSpacing=7;
			_bgHeight=14;
			_bitColor=0;
			_bitOffsetFromBottom=3;
			_bitOffsetFromTop=3;
			_frames=3600;
			
			/* copy above */
			
			// set the threshold. If the value is 50, pixel with color value of 255*50% ( = 127) or higher is decoded as "1"
			_threshold = 50;
			
			
			// calculate how many bits are used
			_frames = _frames.toString(2).length;	
			
			//create a videoplayer
			_videoPlayer = new VideoPlayer();
			_videoPlayer.addEventListener(VideoPlayer.READY_TO_PLAY,video_READY_TO_PLAY);
			_videoPlayer.addEventListener(VideoPlayer.PLAY_PROGRESS,video_PLAY_PROGRESS);

			// load the video from given path
			_videoPlayer.load("FrameNumberedVideo.flv");
			
			
			//create a contentLoader and load an image with it if you want to test with a screen capture first
			_cl = new ContentLoader();
			_cl.addEventListener(Event.COMPLETE,cl_COMPLETE);
			//_cl.load("screenshot.png");

		}
		
		// video is loaded, add trackers etc
		public function video_READY_TO_PLAY(e:Event){
			
			_videoPlayer.playVideo(1);

			// 3 ways to remove bits from view:
			// 1: addView (copies pixels from the video and never shows the actual video)
			// 2: addBlocker adds a solid colored block over the added pixels
			// 3. addMask masks the video
			
			// add video child ONLY if addView is not used!!!
			addChild(_videoPlayer);
			
			
			//addView(_videoPlayer,_bgHeight);
			//addBlocker(_videoPlayer,_bgHeight,0xffffff);
			//addMask(_videoPlayer,_bgHeight);
			
			// show the calculated binaries and frames number over the video
			addBitTracker(_videoPlayer,_bgHeight);
			
			// show the slice cut from the video that is used for detecting binaries. Slice is placed under the video
			addByteSlice(_videoPlayer);

		}
		
		// image is loaded, calculate bits and frame
		public function cl_COMPLETE(e:Event){
			
			addChild(_cl.content);
			
			//get the slice where bits are drawn and read
			
			slice(_bmd,_cl.content,_bitHeight,_bitOffsetFromBottom);
			
			//read bits
			var pixelBits = readPixels(_bmd,_frames,_bitWidth,_bitSpacing,_bitColor,_threshold);

			//covert to integer
			var frame = parseInt(String(pixelBits), 2);
			
			//trace(bin+"/"+pixelBits );
			
			// see function video_READY_TO_PLAY for explanation about function class below
			//addMask(_cl.content,_bgHeight);
			//addBlocker(_cl.content,_bgHeight,0xffffff);
			addBitTracker(_cl.content,_bgHeight);
			addByteSlice(_cl.content);
			
			// if addByteSlice is run, draw the slice
			if(_byteSlice)_byteSlice.bitmapData = _bmd;
			
			//if addBitTracker is run, show the binary and frame
			if(_tf) _tf.text = "Frame:"+frame+", binary:"+pixelBits
		}


		
		public function video_PLAY_PROGRESS(e:Event){

			//save color values in case error occurs
			_lastValues = [];
			//get the slice where bits are drawn and read
			slice(_bmd,_videoPlayer.videoLoader,_bitHeight,_bitOffsetFromBottom);
			//read bits
			var pixelBits = readPixels(_bmd,_frames,_bitWidth,_bitSpacing,_bitColor,_threshold);
			//covert to integer
			var frame = parseInt(String(pixelBits), 2);
			//trace("frame:"+frame+",pixelBits:"+pixelBits);
			// if frame is smaller that before and error has occured
			
			if(frame < _lastBin) {
				
				trace("##### current frame went backwards from "+_lastBin+" to "+frame);
				
				//stop the video so you can check the pixels and error, comment this out later
				//frame = _lastBin++; //error correction
				_videoPlayer.pause();
				for(var i=0;i<_lastValues.length;i++){
					var px = _lastValues[i];
					trace("color:"+px);
				}
			}
			else if(_lastBin > -1 && Math.abs(_lastBin-frame) != 1){
				//trace("##### current frame skipped frames from "+_lastBin+" to "+frame);
			}
			
			_lastBin = frame;
			
			// if addByteSlice is run, draw the slice
			if(_byteSlice) _byteSlice.bitmapData = _bmd;
			// if addView() is run, draw the video frame to it
			if(_view) drawView(_videoPlayer,_bgHeight);
			//if addBitTracker is run, show the binary and frame
			if(_tf) _tf.text = "Frame:"+frame+", binary:"+pixelBits

			
		}
		
		//mask out the added slice
		public function addMask(obj:DisplayObject,bgh:uint){
			var mask:Sprite = new Sprite();
			mask.graphics.beginFill(0xFF0000);
			mask.graphics.drawRect(0, 0, obj.width, obj.height-bgh);
			mask.x = obj.x;
			mask.y = obj.y;
			
			addChild(mask);
			obj.mask = mask;
			
			
		}
		
		//draw over the added slice
		public function addBlocker(obj:DisplayObject,bgh:uint,color:uint){
			_blocker = new Bitmap();
			_blocker.bitmapData = new BitmapData(obj.width, bgh,false,color);
			
			_blocker.x = obj.x;
			_blocker.y = obj.y+obj.height-bgh;
			
			addChild(_blocker);
			
			
			
		}
		
		//dont show video, copy paste pixels without the added slice
		public function addView(obj:DisplayObject,bgh:uint){
			_view = new Bitmap();
			_view.bitmapData = new BitmapData(obj.width, obj.height-bgh,false,0x000000);

			addChild(_view);

		}
		
		//get new frame as video plays on
		public function drawView(obj:DisplayObject,bgh:uint){

			var mat:Matrix = new Matrix();
			var rect = new Rectangle(0,0,obj.width,obj.height-bgh);

			_view.bitmapData.draw(obj, mat,new ColorTransform(),null,rect,false);

		}
		
		//show the frame and binary over the video
		
		public function addBitTracker(obj:DisplayObject,bgh:uint){
		
			_tf = new TextField();
			var textFormat = new TextFormat();
			textFormat.size = 10;
			textFormat.color = 0xFFFFFF;
			textFormat.font = "Arial";
			textFormat.align = "right";
			
			_tf.autoSize = TextFieldAutoSize.NONE;
            _tf.selectable = false;
			_tf.defaultTextFormat = textFormat;
			_tf.width = 200;
			_tf.height = 40;
			_tf.x = obj.width-_tf.width ;
			_tf.y = obj.height-bgh-_tf.height;
			//_tf.border = true;
			addChild(_tf);
		
		
		}
		
		// show the slice  used for binary calculation
		public function addByteSlice(obj:DisplayObject){
			_byteSlice = new Bitmap();
			addChild(_byteSlice);
			_byteSlice.x = 0;
			_byteSlice.y = obj.height;
			
		}
		
		
		// get the slice  used for binary calculation
		public function slice(bmd:BitmapData,obj:DisplayObject,bh:uint,bob:uint){
			
			if(!bmd) bmd = _bmd = new BitmapData(obj.width,1,false,0x000000);
			
			
			var mat:Matrix = new Matrix();
			
			mat.translate(0,-obj.height+bob+Math.floor(bh/2));
			var rect = new Rectangle(0,0,obj.width,1);

			bmd.draw(obj, mat,new ColorTransform(),null,rect,false);
			
			
			
			
			
			
		}

		// calculation binaries from color values
		public function readPixels(bmd:BitmapData,bits:uint,bw:uint,bs:uint,color:uint,threshold:uint):String{
				var px;
				var bin = "";
				var xpos
				
				for(var i=0; i<bits;i++){
					xpos = bmd.width - Math.ceil(bw/2)-((bw+bs)*i)
					
					px = bmd.getPixel(xpos,0);
					
					
					if(color == 0) px = (px >> 16) & 0xFF // RED
					else if(color == 1) px = (px >> 8) & 0xFF //GREEN
					else px = px & 0xFF //BLUE

					_lastValues.push(px);
					if(px >= 255*threshold/100){
						bmd.setPixel(xpos,0,0xFFFF00);
						bin = "1"+bin;
						
					}
					else {
						bin = "0"+bin;
						bmd.setPixel(xpos,0,0x0000FF);
					}

				}
				
				return bin
		
		}
		
		
		
	}	
	
}

Download AE project and Flash files (videos not included)
Download full source: videos, AE project and Flash files

The video used in the examples is © Canon. You can get the video here.

The workflow to make this work is:

– make a new AE project
– import the video
– run the AE script
– copy parameters to Flash
– render the video
– insert the file path to Flash
– Publish Flash and watch the Flash decode the frame numbers in real time
– check if Flash traces any warnings
– if there are warnings, adjust shape size and height and run the AE script again

Have fun!

The increasing abuse of Facebook Pages

Every company is in Facebook nowadays. It is a powerful, easy-to-use and free platform that enables companies to connect with customers, promote products and services, get attention and, alas, abuse it as a promotional tool. Recently I’ve come across more and more advertisements, where the company is promoting their products via Facebook Pages and requiring people to like their page to win prizes. Well, this is not allowed according to Facebook’s Promotion Guidelines:

You cannot: Administer a promotion that users automatically enter by becoming a fan of your Page.

You cannot: Condition entry in the promotion upon a user providing content on Facebook, such as making a post on a profile or Page, status comment or photo upload.

Most people do not know these rules. They probably won’t even think that rules exist, because so many pages are breaking them so it seems that you can do anything you like. Audi Finland was the first major company in Finland who broke the rules with a very appealing competition (become a fan and win an Audi), Facebook grunted and Audi made an excellent save by apologizing in public and making a new competition that didn’t break the rules. Very good job Audi, but it seems that elsewhere the vastly growing tribe of social media ”gurus” didn’t learn from this.

When I met a presentative of the Facebook few months ago, he said that these kind of ”become a fan and win”-promotions are a problem mainly in Finland. Why? Don’t we have more creative ideas? Is the main target of the advertising/marketing agencies just to get 5 ooo fans and then the client has a firm grip on social media and the campaign is a success? The presentative said that Facebook don’t want to be a promotion platform. Facebook is about real relationships and meaningful dialog. I agree  shouting ”Pick me! Pick me and win! I don’t care who you are, but I can give you something for FREE” won’t result in real fans that you could engage with and gain something new. You get few thousand mute fans that don’t give you anything. Yes, of course you get new contacts and a new way to spam them, but that’s not what Facebook is about.

A couple of weeks ago Booky.fi started their promotion in Facebook and radio which says (translated from Finnish): ”everyone who likes our page between 15.-28.11. participates in a sweepstake and we raffle off one book everyday”. That was such a blatant abuse that I had to comment. Their answer was ”No, people don’t automatically attend the sweepstake by liking our page. They have to fill a form which is the way Facebook prefers.” True, but I asked isn’t saying ”everybody who likes our page can win” misleading if a form has to be filled too. They didn’t answer after that.

I did a quick search and found quite many pages that brake the rules: ElamysLahjat, Aqvia, FullMoon, Arpanappula, ON24, Lentovertailu, Matkalehti and KotiMikro. Most likely they are not doing it on purpose, but some of these are surely designed by a SoMe guru promising great results with ridiculously low costs. Facebook is a gold mine for out-of-the-box consultants. But so what if they brake the rules?

Facebook’s Promotion Guidelines states: We may remove any materials relating to the promotion or disable your Page or account if we determine that you violate these Promotions Guidelines, the Statement of Rights and Responsibilities or any other of our policies.

I hope some new marketing people read this and don’t create a campaign that could ban their client from Facebook.

PS. Here is a poor, self-promoting post about how to select a good SoMe expert.

http://www.1goodreason.com/blog/blog/2010/11/18/9-point-social-media-expert-evaluation/

and here is a superb response to that post

http://socialmediatoday.com/dannybrown/239141/9-points-why-i-m-not-social-media-expert

Adaptive search for easy and fast discovery

Search on a large webshop site usually returns a lot of unwanted results. Especially searching with general terms when you don’t know or remember exactly what you are looking for (i.a. ”shirt”, ”shoes”). In this example the user is looking for a pair of sneakers. Searching with ”sneakers” in Amazon returns over 50 000 results. And specifying your search isn’t very easy.

An adaptive search would make the next steps toward discovering the correct product easier and faster.  Each item on the search results would have 2 options: remove an item from results and narrow results down to certain items. Jatka lukemista ”Adaptive search for easy and fast discovery”

CSS sprites with AS3

CSS sprites is a very convenient way to use images. Graphics are placed into one file and CSS defines which part of the image is shown on mouse over and mouse click. One image could hold all graphics used on a website.

Flash has skins for components and the SimpleButton class is quite close to CSS Sprites. I wanted a simpler way to create buttons and use graphics. My IconButton class extends the SimpleButton and slices the given image and sets those slices as upState, overState and downState. HitState is always the upState. IconButton has one extra state: disabled.

MultiIconButton is an extended IconButton. Like its name implies, it has multiple IconButtons. Well, code-wise it has only one, but it has different phases so one image can have multiple different icons for various uses. The illustration below explains the principle. Jatka lukemista ”CSS sprites with AS3”

Very handy HTML5 & CSS3 checker: Modernizr.js

Modernizr detects which HTML5 and CSS3 features user’s browser supports and reports them in a very convenient way: it adds classes to the body tag. The supported features can be checked with javascript, but reporting features with CSS classes is ingenious! Developer don’t have to write if…else -conditions all over javascript because validation can be done in the stylesheet.
Jatka lukemista ”Very handy HTML5 & CSS3 checker: Modernizr.js”

Micropayments vs. micro-earnings

Micropayments are coming like a rollercoaster: faster, slower, faster, slower, up, down up, and making more or less noise. But, eventually, micropayments will be in our everyday lives. App Store is one kind of micropayment site, but true micropayments are just few cents or maybe millicents. They are collected when accessing magazine and newspaper articles, playing games and donating to charity.

But it should be a two way street and the money should also move to the users’ virtual pockets. Micro-earnings are more interesting than payments!
Jatka lukemista ”Micropayments vs. micro-earnings”

The simplest way to send an image from Flash to PHP without user interaction

With the FileReference class, it is quite easy to send an image to the server. But it requires user interaction. What if you need to send an image the user has drawn or captured form the webcam? Then send the data as a ByteArray and let the PHP save the raw data.

The bitmapdata of the image must be encoded to jpg or png and Adobe has classes for that: com.adobe.images.PNGEncoder and com.adobe.images.JPGEncoder. The contentType of the request must be ”application/octet-stream” and if you want to send parameters (file format, username, etc.), add them to the url as parameters: ”simpleSaveImage.php?fileformat=png&filename=cheetach.png”.

In the source files I’m using my ContentLoader to send the data. The sendBytes method  handles everything for you, but here’s also a summary of how to do it without the ContentLoader:
Jatka lukemista ”The simplest way to send an image from Flash to PHP without user interaction”