Friday, September 10, 2010

[Gd] License Evolution and Hosting Projects on Code.Google.Com

| More

Google Code Blog: License Evolution and Hosting Projects on Code.Google.Com

Nearly 6 years ago when we first started thinking about doing project hosting on we noticed something particular about the other open source project hosting sites. They either accepted all Open Source Initiative (OSI) approved licenses, like Sourceforge, or they only accepted one, like the Free Software Foundation's Savannah project, which only accepted GPL'd projects.

In our day-to-day work looking after open source licensing, we lamented the proliferation of licenses and decided that we would split the difference and only offer a very limited subset of the approved OSI licenses choices to our users as a stand against the proliferation of the same. You see, we felt then and still feel now that the excessive number of open source licenses presents a problem for open source developers and those that adopt that software. Thus when we launched project hosting on, we only launched with a small subset of licenses.

This was hardly a barrier to adoption. While there were some complaints from some corners, in the intervening 5+ years since then, we've grown to become one of the largest hosts while allowing that ethic behind license choice to persist.

What's changing and why change now?

We've added an option to the license selector to allow any project to use an OSI approved license. Simply select “other open source” and indicate in your LICENSING, COPYING or similar file which license you are using.

Public domain projects are still only allowed on a case by case basis, as true public domain projects are quite rare and, in some countries, impossible. We encourage those that want to truly ship public domain to look at how Dr. Richard Hipp does things around SQLLite and emulate his style. Email if you’d like to request that license be applied to your project.

(Please note: we will continue to hunt down and kill non-open source projects or other projects using Google Code as a generic file-hosting service.)

Why change now? The TL;DR version is that we think we've made our point and that this new way of doing things is a better fit to our goal of supporting open source software developers.

The longer form of the reason why is that we never really liked turning away projects that were under real, compatible licenses like the zlib or other permissive licenses, nor did we really like turning away projects under licenses that serve a truly new function, like the AGPL. We also think that there were inconsistencies in how we handled multi-licensed projects (for instance: a project that is under an Apache license, but has a zlib component.)

To rectify this, we decided to add an additional option to the license selector that would accommodate some flexibility around open source licenses. We hope you find it useful and look forward to seeing how you use the site!

By Chris DiBona, for the Project Hosting Team

[Gd] Test Open House in New York

| More

Google Testing Blog: Test Open House in New York

Google is holding a testing event in our NY office Wednesday, September 15 at 5:30pm. This includes a tour of our local offices and a live talk on how Google does testing by our own James Whittaker. Rumor has it he's using an early version of his GTAC talk. Lots of food, drink and Google giveaways.

[Gd] One Screen Turn Deserves Another

| More

Android Developers Blog: One Screen Turn Deserves Another

[This post is by Dan Morrill, Open Source & Compatibility Program Manager. — Tim Bray]

Android has an API for accessing a variety of sensor types, such as an accelerometer or light sensor. Two of the most commonly-used sensors are accelerometers and magnetometers (that is, compasses.) Applications and devices frequently use these as forms of user input, and to determine which way to orient the screen.

However, there’s a new wrinkle: recently, a few devices have shipped (see here and here) that run Android on screens that are naturally landscape in their orientation. That is, when held in the default position, the screens are wider than they are tall. This introduces a few fairly subtle issues that we’ve noticed causing problems in some apps. Now, part of the reason for this is that the Android SDK docs on the sensor API left a couple things unsaid, leading many developers to use them incorrectly. Even a couple of our own samples did the wrong thing. Sorry about that!

Fortunately, using these APIs correctly is pretty simple, if you keep three rules in mind:

  • The sensor coordinate system used by the API for the natural orientation of the device does not change as the device moves, and is the same as the OpenGL coordinate system.

  • Applications must not assume that the natural orientation is portrait. That's not true on all devices.

  • Applications that match sensor data to on-screen display must always use android.view.Display.getRotation() to map sensor coordinates to screen coordinates — even if their manifest specifies portrait-only display.

If you have a strong background in math, the three rules above may be all you need to work out the rest. But if that’s not you, the rest of this post explains things step-by-step, and gives some tips for using sensors correctly.

The Basic Problem

Before we dive in, here’s a tip that I personally have found to be helpful: always remember that the sensor data’s coordinate system never changes. Ever. The rest of this post is going to talk about coordinate systems and rotations and so on. But sometimes when your head is deep in 3D transforms, you can get disoriented, so I’ve found it helps to frequently remind myself that no matter what is happening to the screen, the sensor coordinate system never changes.

Now with that tip in mind, we need an example to talk about. Let’s consider a simple app that draws an arrow that always points in the direction of gravity, animating the arrow as the user moves the phone around, like a plumb-bob. When a typical phone is held normally, the arrow points down, as shown in Figure A:

(Note: In the figures in this post, the letter “G” means the direction of gravity in the sensor coordinate system. In Figure A, for example, “G = -y” means that gravity is aligned with the device’s negative-Y axis, as measured by the accelerometer. And remember — the sensor coordinate system never changes!)

This app is pretty straightforward to implement in OpenGL: you simply need to draw an arrow on a GL SurfaceView, after rotating the coordinate space in response to the sensor data returned by the accelerometer. This “just works” because — in this basic case — the OpenGL screen coordinate system lines up with the sensor coordinate system.

So, this technique works, and the arrow will always point down — until you turn the phone too far.

So What’s the Problem?

Most Android devices use the accelerometer to detect when the device is being held sideways, and rotate the screen accordingly. This normally causes the apps to display horizontally, from the point of view of the user.

What this reorientation actually does is remap the X and Y axes, causing the app to draw itself horizontally. However, the Android sensor APIs define the sensor coordinate space to be relative to the top and side of the device — not the short and long sides. When the system reorients the screen in response to holding the phone sideways, the sensor coordinate system no longer lines up with the screen’s coordinate system, and you get unexpected rotations in the display of your app. Figure B shows an example:

There are are a couple different fixes for this problem that are commonly used today, but we’ve noticed that these often don’t work properly on landscape-default devices.

A common first attempt to solve the auto-rotation problem is to simply lock the screen to portrait mode, via the android:screenOrientation attribute in AndroidManifest.xml. This prevents the system from performing a screen coordinate system remap in response to device orientation, and so the sensor and screen coordinate systems remain in sync. However, locking the screen to portrait mode this way prevents the coordinate systems from getting out of sync on portrait-default devices, but causes them to become out of sync on landscape-default devices. This is because it forces a screen reorientation on those devices.

The second common technique is to detect when the device is in landscape mode, and compensate for it by adding a rotation to the graphics that are displayed. Unfortunately, this technique is often only a partial fix, because if you aren’t careful about detecting landscape mode, you will again cause an unnecessary compensation on landscape-default devices.

The Correct Fix

So what’s a poor developer to do? This seems like a catch-22: you can’t prevent screen reorientation, but you can’t compensate for it, either.

Or can you? Actually, you can compensate — you just have to make sure you’re correctly detecting when compensation is necessary. The question is, how does the device tell you that it’s been reoriented? And the answer is: android.view.Display.getRotation().

That method will return one of four values, indicating that either the device has not been reoriented (ROTATION_0), or that it has been reoriented by 90 degrees, 180 degrees, or 270 degrees (which respectively are ROTATION_90, ROTATION_180, and ROTATION_270.)

Pay special attention to those last two. ROTATION_180 and ROTATION_270 mean that each device actually has two portrait and two landscape modes: normal portrait and landscape, and the upside-down versions of each. Some Android devices that do “360 reorientation” will use these rotation modes as well, so you need to handle this generally, beyond just accounting for portrait or landscape mode.

Once you have the screen orientation info in hand, you can treat it as a rotation around the screen’s Z axis when rendering graphics. By applying the rotation to the values you get from your SensorEventListener, you can correctly and reliably compensate for screen reorientations on all devices.

Note that Display.getRotation() will tell you if the screen has been reoriented at all, not that it was reoriented specifically in response to the accelerometer. For example, even if you disable accelerometer-based reorientation by using android:screenOrientation=”nosensor", your app might still be reoriented if the user has opened a hard keyboard on the device.

Because handling all this involves some math that can be a bit of a chore, as a convenience we’ve provided the android.hardware.sensor.SensorManager.remapCoordinateSystem() method to do much of this remapping work for you. If you choose not to do use this method, you can achieve a similar effect by essentially swapping axes, along with the rule of thumb that 2 axis swaps requires that you negate the third axis. (Since this is a bit error-prone, we do recommend that you use remapCoordinateSystem() when you can.)

Recipes for Sensuous Delights

Okay, now we’ve got a technique that we can rely on to work on all devices. But how do you update your app? To give you a more explicit helping hand on how to fix your apps, I’ve whipped up a few recipes for updating your apps.

Apps That Never Draw Sensor Data

Apps that never display graphics derived from sensor data usually don’t need to make any changes. Examples of this type of app are those that detect for bumps to the device, those that use sensors for gesture input, apps that monitor g-forces (watching for free-fall or acceleration), and so on. These apps aren’t drawing images that vary according to the device’s orientation.

This isn’t a hard and fast rule; there probably are some apps out there that do need to take screen orientation into consideration, even though they don’t draw graphics depicting the sensor data. But, if your app just uses sensors in the background, there’s a good chance you won’t need to make any changes.

Apps That Work in Both Portrait and Landscape

Most Android apps work fine in both portrait and landscape, using the standard tools. If your app is one of these and you also use sensors, the only change your app probably requires is a tweak to use the behavior I outlined above. That is:

  • Don’t assume that portrait is the default mode.

  • Don’t assume that locking your app to portrait mode solves this issue.

  • Don’t assume that disabling sensor-based reorientation solves this issue (since reorientations also occur on some devices when the user opens a keyboard.)

  • Check for the current device orientation via getRotation(), and compensate accordingly, as detailed earlier.

Apps That Only Work in One Orientation

Some apps — notably, many games — only work well (or at all!) in either portrait or landscape mode. It’s perfectly okay, of course, for such apps to lock themselves to the appropriate mode, and doing so simplifies the sensors quite a bit.

However, because Android devices actually support two landscape and two portrait modes, these apps still need to check the current orientation. That is, if an app locks itself to landscape mode, it will need to perform a compensation on portrait-default devices, but not on landscape-default devices. And of course — are you sick of hearing this yet? — this can be accomplished by checking the result of getRotation().

Phew! Quite a mouthful for what is a fairly straightforward notion, once you understand what’s going on. But if I had to distill all that down into a single sentence, it would be this: android.view.Display.getRotation() is your friend.

I hope you’ve found this information useful; what’s more, I hope you’ve found it practical. We’ll keep improving our SDK and docs, and I hope you’ll keep improving your apps.

Happy coding!


Thursday, September 9, 2010

[Gd] Unifying content under multilingual templates

| More

Official Google Webmaster Central Blog: Unifying content under multilingual templates

Webmaster Level: Advanced

If you have a global site containing pages where the:
  • template (i.e. side navigation, footer) is machine-translated into various languages,
  • main content remains unchanged, creating largely duplicate pages,
and sometimes search results direct users to the wrong language, we’d like to help you better target your international/multilingual audience through:

<link rel=”alternate” hreflang="a-different-language" href="http://url-of-the-different-language-page" />

As you know, when rel=”canonical” or a 301 response code is properly implemented, we become more precise in clustering information from duplicate URLs, such as consolidating their linking properties. Now, when rel=”alternate” hreflang=”x” is included in conjunction with rel=”canonical” or 301s, not only will our indexing and linking properties be more accurate, but we can better serve users the URL of their preferred language.

Sample configuration that’s prime for rel=”alternate” hreflang=”x”

How does this all work? Imagine that you’re the proud owner of, a site called “The Network” where you allow users to create their very own profile. Let’s say Javier Lopez, a Spanish speaker, makes his page at

Because you’re trying to target a multilingual audience, once Javier hits “Publish,” his profile becomes immediately available in other languages with the translated templates. Also, each of the new language versions is served on a separate URL.

Two localized versions, in English and in French

Background on the old issue: duplicate content caused by language variations

The configuration above allowed visitors speaking different languages to more easily interpret the content, but for search engines it was slightly problematic: there are three URLs (English, French, and Spanish versions) for the same main content in Javier’s profile. Webmasters wanted to avoid duplicate content issues (such as PageRank dilution) from these multiple versions and still ensure that we would serve the appropriate version to the user.

A new solution for localized templates

First of all, just to be clear, the strategy we’re proposing isn’t appropriate for multilingual sites that completely translate each page’s content. We’re trying to specifically improve the situation where the template is localized but the main content of a page remains duplicate/identical across language/country variants.

Before we get into the specific steps, our prior advice remains applicable:
  • Have one URL associated with one piece of content. We recommend against using the same URL for multiple languages, such as serving both French and English versions on based on user information (IP address, Accept-Language HTTP header).

  • When multiple languages are at play, it’s best to include the language or country indication in the URL, e.g., and (which specify “en” and “fr”) rather than and (which don’t contain an explicit country/language specification). More suggestions can be found in our blog posts about designing localized URLs and multilingual sites.
For the new feature:
Step 1: Select the proper canonical.
The canonical designates the version of your content you’d like indexed and returned to users.
The first step towards making the right content indexable is to pick one canonical URL that best reflects the genuine locale of the page’s main content. In the example above, since Javier is a Spanish-speaking user and he created his profile on, is the logical canonical. The title and snippet in all locales will be selected from the canonical URL.

Once you have the canonical URL picked out, you can either:
A. 301 (permanent redirect) from the language variants to the canonical

As an example, if a French speaker visits (not the canonical), have this page include a cookie to remember the user's language preference of French. Then permanently redirect from to the canonical at Because of the cookie, will still render its boilerplate in French (even on the subdomain!). Similarly, would set the value of this cookie to English and then 301 redirect to

Including a language selection link is also helpful should a multilingual user prefer a different experience of your site.

B. Use rel=”canonical”

On the other language variants, include a link rel=”canonical” tag pointing to your chosen canonical. In our example, since the canonical for Javier’s profile is the Spanish version, the English and French pages (and optionally even the Spanish page itself) would include <link rel=”canonical” href="" />.

Cookies are not involved in this setup. Therefore, a French speaker will be served with a Spanish template. Implement step 2 if you want the French speakers to be served the French version at in Google search results.
Step 2: In the canonical URL, specify the various language versions via the rel=”alternate” link tag, using its hreflang attribute.

rel=”alternate” URLs can be displayed in search results in accordance with a user’s language preference. The title and snippet, however, remain generated from the canonical URL (as is customary with rel=”canonical”), not from the content of any rel=”alternate”.
You can help Google display the correctly localized variant of your URL to our international users by adding the following tags to, the selected canonical:

<link rel=”alternate” hreflang="en" href="" />

<link rel=”alternate” hreflang="fr" href="" />

rel=”alternate” indicates that the URL contains an alternate version located at the URI of the href value. hreflang identifies the language code of the alternate URL and can be specified with ISO-639.

Please note: If your site supports many languages and you’re worried about the increased file size when declaring numerous rel=”alternate” URLs, please see our Help Center article about configuring rel=”alternate” with file size constraints.
Once the steps are completed, the configuration on “The Network” would look like this:
    either 301s with a language cookie or contains <link rel=”canonical” href=”” />
    either 301s with a language cookie or contains <link rel=”canonical” href=”” />
    is the canonical and contains
    <link rel=”alternate” hreflang="en" href="" />
    <link rel=”alternate” hreflang="fr" href="" />

Results of the above implementation
  • When your content is returned in search results, users will likely see the URL that corresponds to their language preference, whether or not it’s the canonical. (Good news!) This is because with with rel=”canonical” or a 301 redirect, we can cluster the language variations with the canonical. With rel=”alternate” hreflang=”x” at serve-time we can deliver the URL of the most appropriate language to the user: English speakers will be served as the result for the URL in Javier’s profile, French speakers will see, Spanish speakers will see

  • By implementing step 1, only content from the canonical version will be available for users in search results (i.e. content from the duplicate versions won’t be searchable). Because the Spanish version is the canonical, queries that include template content from this page, e.g. [Javier Lopez familia] -- when using any language preference -- may return his profile (content from the canonical version). On the other hand, queries that include template content of the “duplicate” version, e.g. [Javier Lopez family], are less likely to return his profile page. If you would like the other language versions indexed separately and searchable, avoid using rel=”canonical” and rel=”alternate”.

  • Indexing properties, such as linking information, from the duplicate language variants will be consolidated with the canonical.

To recap (one more time, with feeling!)

For sites that have their template localized but the keep their pages’ main content untranslated:

Step 1: Once you have the canonical picked out you can use either rel=”canonical” or a 301 (permanent redirect) from the various localized pages to the canonical URL.

Step 2: On the canonical URL, specify the language-specific duplicated content with different boilerplate via the rel=”alternate” link tag, using its hreflang attribute. This way, Google can show the correctly-localized variant of your URLs to our international users.

We realize this can be a little complicated, so if you have questions, please ask in our webmaster forum!

Written by Surabhi Gupta, Joachim Kupke & Jayesh Vyas, Search Localization and Indexing Teams

[Gd] Discover v201003: Using Ad Sitelinks

| More

AdWords API Blog: Discover v201003: Using Ad Sitelinks

Ad Sitelinks is a feature for search-based ads that allows you to extend the value of your existing AdWords ads by providing additional links to specific, relevant content deep within your website. Rather than sending all users to the same landing page, Sitelinks can provide up to 4 additional links for the user to choose from, as shown below.

(View larger image)

By providing users with more options, you can create richer, more relevant ads that improve the value of your brand terms and other targeted keywords. You can learn more about Sitelinks in the AdWords Help Center.

Using Ad Sitelinks in AdWords API v201003
Sitelinks is supported starting with the v201003 version of the AdWords API. Sitelinks is implemented as campaign ad extensions, and can be managed using the CampaignAdExtensionService. Keep in mind that each campaign can have only one CampaignAdExtension containing a SiteLinkExtension. This means that prior to adding Sitelinks to your campaign, you should check if the campaign already has one, and remove it if does. The C# code snippet below shows how to retrieve the campaign ad extension containing active Sitelinks for a given campaign.

private long? GetActiveSitelinkExtension(long campaignId) {
 long? siteLinkExtensionId = null;

 // Get the campaign ad extension containing sitelinks.
 CampaignAdExtensionSelector selector = new CampaignAdExtensionSelector();
 selector.campaignIds = new long[] { campaignId };
 selector.statuses = new CampaignAdExtensionStatus[] {
CampaignAdExtensionStatus.ACTIVE };

 CampaignAdExtensionPage page = campaignExtensionService.get(selector);
 if (page != null && page.entries != null) {
   foreach (CampaignAdExtension extension in page.entries) {
      if (extension.adExtension is SitelinksExtension) {
         siteLinkExtensionId =;
 return siteLinkExtensionId;

If GetActiveSitelinkExtension returns null, then you can remove the existing SitelinkExtension as shown below.

private SitelinksExtension RemoveActiveSitelinkExtension(
   long campaignId, long siteLinkExtensionId) {
 CampaignAdExtension campaignAdExtension = new CampaignAdExtension();
 campaignAdExtension.campaignId = campaignId;
 campaignAdExtension.adExtension = new AdExtension(); = siteLinkExtensionId;

 CampaignAdExtensionOperation operation = new CampaignAdExtensionOperation();
 operation.@operator = Operator.REMOVE;
 operation.operand = campaignAdExtension;
 CampaignAdExtensionReturnValue retVal =
     campaignExtensionService.mutate(new CampaignAdExtensionOperation[] {
         operation });
 if (retVal != null && retVal.value != null && retVal.value.Length > 0 &&
     retVal.value[0].adExtension is SitelinksExtension) {
   return (SitelinksExtension) retVal.value[0].adExtension;
 } else {
   return null;

You can now add new siteLinks as shown below.

private SitelinksExtension AddSitelinks(long campaignId, Sitelink[]
   siteLinks) {
 SitelinksExtension siteLinkExtension = new SitelinksExtension();
 siteLinkExtension.sitelinks = siteLinks;

 CampaignAdExtension campaignAdExtension = new CampaignAdExtension();
 campaignAdExtension.adExtension = siteLinkExtension;
 campaignAdExtension.campaignId = campaignId;

 CampaignAdExtensionOperation operation = new CampaignAdExtensionOperation();
 operation.@operator = Operator.ADD;
 operation.operand = campaignAdExtension;

 CampaignAdExtensionReturnValue retVal =
     campaignExtensionService.mutate(new CampaignAdExtensionOperation[] {
         operation });
 if (retVal != null && retVal.value != null && retVal.value.Length > 0) {
   return retVal.value[0].adExtension as SitelinksExtension;
 } else {
   return null;

The following snippet puts it all together.

Sitelink siteLink1 = new Sitelink();
siteLink1.displayText = "Birthday Flowers";
siteLink1.destinationUrl = "";

Sitelink siteLink2 = new Sitelink();
siteLink2.displayText = "Deal of the day";
siteLink2.destinationUrl = "";

long? linkId = GetActiveSitelinkExtension(campaignId);
if (linkId != 1) {   
 RemoveActiveSitelinkExtension(campaignId, linkId);
AddSitelinks(campaignId, new Sitelink[] { siteLink1, siteLink2});

As always, the latest API unit costs for adding, removing or or retrieving Sitelinks is available in the AdWords API rate sheet under the CampaignAdExtensionService.

We've included support for Sitelinks in each of our client libraries to help you get started, so please try it out and share your feedback with us on the forum.

-- Anash P. Oommen, AdWords API Team

[Gd] AdWords Downtime: September 11, 10am-2pm PDT

| More

AdWords API Blog: AdWords Downtime: September 11, 10am-2pm PDT

We'll be performing routine system maintenance on Saturday, September 11 from approximately 10:00am to 2:00pm PDT. You won't be able to access AdWords or the API during this time frame, but your ads will continue to run as normal.

- Eric Koleda, AdWords API Team 

[Gd] Web Graphics – Past, Present and Future

| More

Chromium Blog: Web Graphics – Past, Present and Future

Recently, we posted about the work we’re doing to re-architect Chromium’s graphics stack and use the GPU to accelerate rendering. As we mentioned last time, this work will help ensure that developers can take full advantage of emerging graphics standards like 3D CSS and WebGL in Chromium. To get more feedback about these cool new features, we’re enabling hardware compositing along with 3D CSS transforms and WebGL on the trunk (coming soon to the dev channel). These new capabilities are major additions to the web platform, so we wanted to take the time to provide some background information and explain how these new capabilities fit into the web.

SVG and canvas: dynamic 2D

Until recently, it wasn’t possible to create any dynamic (i.e. non-image) graphics on the web without a plug-in. Starting in 2005, this began to change as browsers began to add Scalable Vector Graphics (SVG) and HTML 2D canvas element support. Both SVG and 2D canvas allow you to compose a 2D image at run time and manipulate it to achieve animation effects, but they vary greatly in their approach to specifying how you draw an image.

<?xml version="1.0"?>
"-//W3C//DTD SVG 1.1//EN"
<svg xmlns="" xmlns:xlink="">
<path style="stroke-width:1; fill:rgb(246,127,0); stroke:none" d="M204.33 13..."/>
<path style="stroke-width:1; fill:rgb(0,0,0); stroke:none" d="M203.62 139.62..." />
<path style="stroke-width:1; fill:rgb(255,246,227); stroke:none"
d="M363.73 85.73 C359.27 86.29 355.23 86.73..."/>

*note: ellipses replace many more points

d = document.getElementById("c");
c = d.getContext("2d");
i = 25;
while (i--) {
q = (R / r - 1) * t;
// create hypotrochoid from current mouse position, and setup variables (see:
x = (R - r) * C(t) + D * C(q) + ...
y = (R - r) * S(t) - D * S(q) + ...
if (a) {
// draw once two points are set
c.moveTo(a, b);
c.lineTo(x, y)
c.strokeStyle = "hsla(" + (U % 360) + ",100%,50%,0.75)";
// draw rainbow hypotrochoid
} *note: ellipses replace code

The images above were created with SVG and canvas, but as you can see from the code, they approach graphics in a very different way: SVG allows you to provide markup that describes an image, whereas canvas allows you to describe a set of sequential steps that draw an image in JavaScript. These approaches mean that a developer changes an image that’s already been drawn, such as when animating an image, in different ways. Because the browser keeps a full representation of an SVG image, changing just a parameter in the image is enough to cause the browser to redraw the image correctly. With canvas, on the other hand, the developer must clear the image and specify all the steps to draw it again with the desired changes.

Today, modern browsers, including Firefox, Safari, Opera and Google Chrome support creating 2D graphics with these technologies, and Internet Explorer is adding support for them in the upcoming version 9 release.

CSS Transforms: easy to use 2D and 3D effects

Even today, people primarily use apps that don’t strictly require advanced graphics, but eye candy like 3D transforms, transitions and reflections still help improve the experience of everyday tasks. While canvas could be used to create many of these effects, it can’t render them efficiently, and it would be hard to integrate with the other content on the page.

CSS transforms and animations, which first appeared in WebKit in 2007, allow developers to achieve commonly used effects easily by specifying parameters in CSS that are applied to content in the DOM. In 2009, WebKit began adding 3D CSS transforms and effects, which takes flat content on the page and makes it appear as if it were in 3D space.

/* CUBE */
#transitions #cube {
-webkit-transform-style: preserve-3d;
width: 600px;
height: 400px;
position: absolute;
#transitions {
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
-webkit-animation-name: cubedemo;
-webkit-transform: rotateX(-90deg);
#transitions #cube .face {
position: absolute;
width: 600px;
height: 400px;
display: block;
overflow: hidden;
#transitions #cube .front {
-webkit-transform: scale3d(.835,.835,.835) translateZ(200px);
#transitions #cube .back {
-webkit-transform: scale3d(.835,.835,.835) rotateY(180deg) translateZ(200px);
#transitions #cube .top {
-webkit-transform: scale3d(.835,.835,.835) rotateX(90deg) translateZ(200px);

@-webkit-keyframes cubedemo {
0% {-webkit-transform: rotateX(0); -webkit-animation-timing-function: linear; }
50% {-webkit-transform: rotateX(-92deg);-webkit-animation-timing-function: ease-in; }
70% {-webkit-transform: rotateX(-84deg); -webkit-animation-timing-function: ease-in; }
80% {-webkit-transform: rotateX(-90deg); -webkit-animation-timing-function: ease-in; }
95% {-webkit-transform: rotateX(-88deg); -webkit-animation-timing-function: ease-in; }
100% { -webkit-transform: rotateX(-90deg); }

As you can see from this example, 3D CSS transforms and animations make it easy to add polished 3D effects to your app. Now that we support hardware compositing in Chromium, it’s easy to perform these transforms on the GPU and display it quickly on screen, so we’ve enabled them along with the compositor. Currently, this functionality is only available in Safari and Google Chrome but Firefox is working on an implementation as well, making it a great option to add impressive effects to your app in the future.

WebGL: Low-level dynamic 3D

While 3D CSS makes it easy to display 2D content so that it looks like it’s in a 3D space, it’s not designed for writing true 3D applications like CAD software or modern games. WebGL, on the other hand, provides access to all the functionality of OpenGL ES 2.0 from JavaScript, and is designed with exactly these types of applications in mind.

(link requires a WebGL-enabled browser)

(link requires a WebGL-enabled browser)

With WebGL you can navigate 3D environments, rotate around objects with volume, add realistic lighting, and render shadows and reflections like those above. Creating a scene like this just wouldn’t be possible in real-time with 3D CSS, let alone a 2D canvas or SVG. To achieve these effects, you need direct access to graphics hardware – which is exactly what WebGL provides.

With power comes complexity, so there is definitely a learning curve to using WebGL. The good news is that because it’s based on OpenGL ES 2.0, it should be familiar to people with graphics programming experience. A number of JavaScript libraries that make WebGL more accessible are already available, for example, the examples above use two frameworks: SpiderGL and the WebGL implementation of O3D. As the technology matures, expect to see other tools and libraries emerge to make it even easier to author content. A popular blog in the community, Learning WebGL has done a great job of keeping up with the latest libraries, tools and demos and has a substantial archive of WebGL resources.

Mozilla, Apple, Opera and Google are all working on putting the finishing touches on the WebGL spec in a Khronos working group, which is expected to hit v1.0 by the end of the year, but we’ve turned it on by default to get early feedback on Chromium’s implementation.

Thanks for reading to the end, we hope this helps explain the current state of graphics on the Web!

Posted by Henry Bridge, Product Manager