The Aviary SDK is now part of the Adobe Creative SDK

The Aviary SDK is now the Image Editing component in the Adobe Creative SDK. Visit to access the latest Image Editing SDK and brand new SDK components by Adobe, offering features like store to Creative Cloud and publish to Behance.

Aviary Web Widget Documentation

Aviary's web widget (codename: Feather) can be embedded in any website with just a few lines of Javascript, adding simple yet powerful image editing to an existing workflow.

The Aviary editor is optimized for the latest versions of Chrome, Firefox, Safari, and Internet Explorer (IE9). We support IE8 but not all features are available for these browsers. Aviary also works in mobile browsers, so it will function on handheld touch-screen devices, but it works best on tablets. For handheld use we generally recommend our native SDKs (iOS, Android), which have a UI optimized for smaller screens and performance tuned to the native device.


Get the widget!

Get started with this boilerplate.

Editor Javascript Url ("feather.js")

SSL-secure page? Please use this location (via Amazon Cloudfront):

Note: Please always call this script directly, as there are some server-side dependencies.

Introduction - High Level

The following describes how the editor works in your webpage in the most basic terms:

  1. Load feather.js
  2. Configure your widget instance with a configuration object
  3. After the DOM is ready and your instance has loaded, launch your instance.

API Overview

The API is inspired by the jQuery plugin pattern, wherein you pass a configuration object of key/value pairs. This allows for the widget to grow and introduce new features without breaking existing implementations or being constrained by its API.


The following configuration parameters can be specified in your iniitalization. See the example integration.

image, apiKey, and apiVersion are required



Either the image element to be edited or its ID.

Note: Make sure the DOM has loaded before attempting select an element to pass as image, or use its ID instead. The image itself must be located in a publicly-accessible web folder, otherwise the widget will not work.



Get your API key from the Get Key page. Saving will not work without it!
Note: when generating your API key, provide your root domain, even if you will be using it on subdomains.



This lets Aviary know which version of the editor to load and allows you some protection from breaking changes. The latest editor version is 3.


Choose from 'dark' or 'light' themes. Default value is 'dark'. You can also use the 'minimum' theme to reduce our styles to the bare minimum in order to more easily add your own styles on top. This removes most textures, shadows, rounded corners, and gradients that can be tedious to reset.

Note: this is new in apiVersion: 3


The widget will be inserted inside the element with the DOM ID specified.

Note: For best results, this should be a <div /> element


The widget's UI is being localized into many languages. You can choose from the following languages in addition to enEnglish (default):

  • bg Bulgarian
  • ca Catalan
  • zh_HANS Chinese (simplified)
  • zh_HANT Chinese (traditional)
  • cs Czech
  • da Danish
  • nl Dutch
  • fi Finnish
  • fr French
  • de German
  • el Greek
  • he Hebrew
  • hu Hungarian
  • id Indonesian
  • it Italian
  • ja Japanese
  • ko Korean
  • lv Latvian
  • lt Lithuanian
  • no Norwegian
  • pl Polish
  • pt Portuguese
  • pt_BR Portuguese (Brazilian)
  • ru Russian
  • es Spanish
  • sl Slovak
  • sv Swedish
  • tr Turkish
  • vi Vietnamese

Note: You can also help us translate your own language! Shoot us an email at if you'd like to send us a translation for a new language, or if you spot something missing or incorrect in our current offerings.


Enabling CORS lets the Editor load your images instantaneously. Otherwise we have to work some magic (by using JSONP image laundering service) to get the image data into the client's browser across domains. We highly encourage you to enable this for a better user experience.

Note: You must setup & add the appropriate CORS headers on the server endpoint that hosts your images. Please see: for more information and how to set it up on your server. If you have any specific questions, please contact us


Set a max size of drawing canvas. See Best Practices.


Set to 'true' to remove the close button.


Set the launch fade-in animation time in milliseconds.


Set the close fade-out animation time in milliseconds.


Select which editing tools you would like to display in the tools panel. We recommend either omitting tools or setting it to 'all' which will automatically give you our most up-to-date selection of recommended tools without updating your integration code.

  • all: selects all Aviary-recommended tools. (default)

You can also specify one or more options by name in an array or comma-separated string. Choose from our latest set of tools below:

  • enhance: Autocorrect your photo with one of four basic enhancements.
  • effects: Choose from a variety of effects and filters for your photo.
  • frames: Choose from a variety of frames to apply around your photo.
  • stickers: Choose from a variety of stickers you can resize and place on your photo.
  • orientation: Rotate and flip your photo in one tool.
  • focus: Adds a selective linear or radial focus to your photo.
  • resize: Resize the image using width and height number fields.
  • crop: Crop a portion of your photo. Add presets via API. Fixed-pixel cropPresets perform a resize when applied.
  • warmth: Adjust the overall image color temperature.
  • brightness: Adjust the overall image brightness.
  • contrast: Adjust the overall image contrast.
  • saturation: Adjust the overall image saturation.
  • sharpness: Blur or sharpen the overall image in one tool.
  • colorsplash: Use a smart brush to colorize parts of your photo which becomes grayscale otherwise.
  • draw: Add doodle overlays with a brush.
  • text: Add custom, resizable text.
  • redeye: Remove redeye from your photo with a brush.
  • whiten: Whiten teeth with a brush. (Not supported in IE8)
  • blemish: Remove skin blemishes with a brush.

Tool Order: the order in which you list the options is the order in which they will appear in the editor. The editor options will be paged, so put the most important functions up front.


The widget can start with a specific tool open by passing its name here.

Note: if you're looking to launch the editor with the Crop tool initialized, you may want to check out forceCropPreset below for a truly "forced crop" experience. initTool:crop will just open with the normal (not forceable) Crop tool.


A comma-separated list of preset crop sizes or ratios for the crop tool. It can be a 2-D array (with labels), a 1-D array (without labels), or a comma-separated string (without labels). Any sizes over your maxSize will be excluded in the UI.


    ['Square', '1:1'],
    '3:2', '5:3', '4:3', '5:4', '6:4', '7:5', '10:8', '16:9'

Preset dimensions are not included by default but can be added:


You can label the presets by providing each as an array:

    ['Photo', '4:3'],
    ['HDTV', '16:9'],
    ['Avatar', '60x50']

Tip: if your users are likely to be sharing to Twitter or Facebook, we recommend including the following presets:

    ['Twitter Preview', '2:1'],
    ['FB Cover Photo', '851x315']


Defaults to false, but if set to true, this will disable the inverting of crop presets. Crop presets can be inverted by the user (e.g. clicking "3:4" to change it to "4:3"), but are also inverted automatically based on the photo's initial orientation.


Defaults to 'Custom'. You can set your default crop to be something else when the tool opens.

Note - if this preset is not found, the first crop preset is then selected.


If you would like to force the user to a specific crop preset (dimension or ratio), you can pass a cropPreset when you use the launch() method. This will force the user into a special version of the crop tool when launched. They will not be able to proceed into the full editor until they apply your predefined crop.


  'image': ...,
  'url': ...,
  forceCropPreset: ['My Size','2:1']  

  'image': ...,
  'url': ...,
  forceCropPreset: ['Profile Picture','150x150']
  forceCropMessage: 'Crop your profile picture:'

Note: If you use forceCrop, the orientation, resize & crop tools will be disabled when the user applies the crop.


If you are using forceCropPreset, you can also enter in a custom message for your users above. The message will appear above the cropPreset label.


forceCropMessage: 'Please crop your photo to this size:'


This is required if the image to be edited is on a different host than the host HTML page. Otherwise it is optional.

Note - this param should be omitted if possible. Providing it is a clue to our API that your image is on another domain and you need to request origin-clean data from our server which adds to image load time. It also requires that the image at the location be publicly available as our server must download it.


Set the saved file format. Accepts: png, jpg. The default behavior is to derive the format from the original file extension.


coming soon
Set the save quality of the image as a JPG file type. Accepts a number between 1 - 100.


Set to true to allow a user to see the actual size of the photo (in pixels) in the bottom left corner of the editor.

Note - This reflects output size and may be different from the original image.


Pass a function to run once the widget has all resources ready for a launch (such as launching as soon as possible after page load). See Best Practices.


Pass a function to be called once the editor has finished launching and is ready for user input.


Pass a function to be called before an image save happens, but after a user has clicked the save button, intending to save.

imageID gives you an ID to reference the original image being edited.

Return false to this handler if you'd like to abort the save process.

Note - You can always asynchronously re-initiate the save using save(). This allows you to present a custom confirmation to the user or wait for some custom processing to complete before the save is initiated.

onSave(imageID, newURL)

Pass a function to be called when the image save is complete.

imageID gives you an ID to reference the original image being edited.

newURL is a link to the saved image. It will persist at this address for approximately 72 hours. Use the newURL to download the edited image back to your own server. It can also be used to set the image in the DOM to point to the new source.

Return false to this handler if you'd like to suppress the built-in save confirmation dialog.


Pass a function to be called when the editor is closed.

isDirty tells whether the editor was closed with unsaved changes.


The API can notify you of errors and you have the option to notify the user. They are otherwise silent.

errorObj is an object with the following properties:

  • code : int representing the error type. For example, 1 corresponds to an image that could not be processed.
  • message : An error message you could pass along to the user.
  • args : This is an array of additional values that varies by error type. For example, error type 1, bad image, will send the problematic URL as the only argument in the array. This could assist in debugging or helping a user correct a specific problem.

Error codes and meanings

  1. Unable to load image from image param provided.
  2. Unable to detect Flash plugin or HTML canvas support. Cannot launch editor.
  3. Image at url param could not be accessed by Aviary service.
  4. Tool requested is not part of your chosen tools or not supported by this browser.
  5. Image cannot be edited due to browser security restrictions. Image must come from the same domain as this page, or its location must be provided to the url param so Aviary server can generate generic image data with no origin.
  6. Image cannot be edited due to same-origin restrictions. (provide a link to it via url)
  7. Problem saving photo. This would usually be temporary if ever.
  8. apiKey not provided.


First, call feather.js from your web page

Next, instantiate the HTML5 editor with a configuration object:

var featherEditor = new Aviary.Feather(configObj);

Once you have created your instance (in this case, featherEditor) you have a few methods at your disposal.


You can open the editor on demand using its launch method:


At this point you can override any of the configuration parameters except for theme, language and onLoad. Or, if all required parameters are provided, you can omit any configuration at this time.

The first two parameters define the HTML and CSS the editor will use. onLoad is no longer valid as the editor has loaded by this time.


You can ask for the current image pixel dimensions on demand through the API


This will return an object with width and height


Show the wait indicator (spinning overlay on top of photo).


Hide the wait indicator (spinning overlay on top of photo).


The save method lets you save progress through some separate UI, such as a button on your hosting page. It is not typically necessary, as the user can always save from within the editor at any time.;


You can shut down the editor programmatically with the close method.

Use with care, as this will discard any unsaved changes without prompting the user. For this reason it is commonly used onSave if at all.

May be useful when noCloseButton is set to true.




<img id="editableimage1" src=""/>


<!-- Load widget code -->
<script type="text/javascript" src=""></script>

<!-- Instantiate the widget -->
<script type="text/javascript">

    var featherEditor = new Aviary.Feather({
        apiKey: '1234567',
        apiVersion: 3,
        tools: ['draw', 'stickers'],
        onSave: function(imageID, newURL) {
            var img = document.getElementById(imageID);
            img.src = newURL;

    function launchEditor(id, src) {
            image: id,
            url: src
        return false;


<!-- Add an edit button, passing the HTML id of the image
    and the public URL to the image -->
<a href="#" onclick="return launchEditor('editableimage1', 

<!-- original line of HTML here: -->
<img id="editableimage1" src=""/>

Saving Your Work

What happens on a save

  1. (Client) User clicks the save button.
  2. (Client) The widget will call any function passed to onSaveButtonClicked, allowing an opportunity to abort the save via API.
  3. (Client) If no function was registered or false was not returned, the widget posts the image data along with configuration parameters to Aviary
  4. (Server) Aviary creates a temporary image from the image data.
  5. (Server) Aviary responds to the widget's client request with a public link to the temporary image.
  6. (Client) On this response, the widget will call any function passed to onSave, and then show a confirmation dialog to the user. You should handle any saving of the edited photo in the onSave callback.

Server-side Examples

All of these examples assume the following onSave callback:
function onSave(imageID, newURL) {
  $.post('/save', {url: newURL, postdata: 'some reference to the original image'})


#postdata: originalName
post "/save" do
    #original photo name is postdata
    orig_name = params[:postdata]
    new_name = orig_name + " (edited with Aviary)"
    # Retrieve a file object from the image URL
    image_from_web  = open(params[:url]) {|f| }
    # get file name from URL
    file_name = params[:url].split("/").pop()
    # Write the file to the local filesystem
    Dir.chdir("tmp"), 'w') {|f| f.write(image_from_web) }




    $image_data = file_get_contents($_REQUEST['url']);




protected void Page_Load(object sender, EventArgs e)
    string postdata = Request["postdata"]; // an encoded reference to the file that you're updating perhaps
    string url = Request["url"]; // the url of the saved file

    string localFileDest = "..."; // file save destination

    WebClient client = new WebClient();
    client.DownloadFile(url, localFileDest);

Best Practices

During integration, log any errors with onError

The plugin will fire internal error messages to a provided onError callback and these could be very revealing if you find the editor failing to load an image or launch.

When to embed the editor in your layout and when to let it fill the screen

You should provide a <div/> element ID to the appendTo parameter when you want full control over the editor experience (i.e. launching the editor in your own custom lightbox). It is best suited for more advanced integrations. Because the editor tries to fill the space alotted it, we recommend you specify a fixed width and height in CSS, as well as a position property of relative, absolute, or fixed so that the container is considered a positioned element.

Do not provide an element to appendTo if you want to show larger images closer to their native size, and/or your layout does not provide suitable space for the editor (at least 714px wide by 400px high). This will launch the editor in a simple lightbox that floats above the page in z-order and temporarily darkens the rest of the page until the editing is complete. It also offers users the most room for tools visible at once. This is generally the recommended approach.

Larger Images

For the best user experience, please note that this lightweight editor is meant to handle file sizes intended for web display, not original resolutions. If you are interested in working with the API in original resolutions, please contact

You can use the maxSize parameter to set the maximum size of the output image and the actual size of the editing canvas. The API only accepts one value that it uses to specify that max height and width of the canvas. This prevents the canvas from getting too wide if a taller image is rotated, for example. The default (recommended) value is 800 (800x800 pixels).

Waiting for the Widget to Initialize

Calling launch() before the widget has loaded and initialized will have no effect. A nice usability enhancement is to disable the "Edit" button and only enable it onLoad. Assuming in the examples above, the "Edit!" link has an ID of "edit-button" and a style of "display: none":

var featherEditor = new Aviary.Feather({
    onLoad: function() {
        document.getElementById('edit-button').style.display = 'block';

Note: Make sure the callback function in either of these examples runs after the DOM is ready and the edit button is accessible with JavaScript. See jQuery's .ready() method for more details.

Custom CSS Styling

Overriding Aviary CSS selectors

Custom CSS styling of the editor is possible. Be sure to make your overrides robust enough to supercede our own styles (usually accomplished by simply prepending body to the selector).

Blank Canvas

Using the minimum theme (theme: 'minimum') can be very helpful if you plan to do heavy customization as it gives you something of a blank canvas.

Style Guide

The widget can be styled according to our public style guide, but hooking into any other markup is not supported so please limit customization to the following:

/* general font color/reset */
body .avpw *,
body .avpw a,
body .avpw a:link,
body .avpw a:hover,
body .avpw a:visited,
body .avpw a:active {
  color: #444;
/* dark background behind image */
.avpw .avpw_canvas_background {
  background: #3e464a url(../images/dark_backdrop.png);
/* dark background behind tools */
.avpw .avpw_inset_background {
  background: #3e464a url(../images/dark_backdrop.png);
/* brushed metal texture */
/* outermost widget body/parent */
body .avpw {
  background: #f0f0f0 url(../images/white_backdrop.png) 50% 0%;
/* small icon inside an open tool */
.avpw .avpw_current_tool_icon {
  background: #f0f0f0 url(../images/white_backdrop.png) 50% 0%;
/* popups that open within the widget */
.avpw .avpw_app_popup {
  background: #f0f0f0 url(../images/white_backdrop.png) 50% 0%;
/* colorpicker popup */
.avpw .avpw_color_picker_container {
  background: #f0f0f0 url(../images/white_backdrop.png) 50% 25px;
/* footer text */
.avpw .avpw_footer_text,
.avpw .avpw_footer_text a,
.avpw .avpw_footer_text a:link,
.avpw .avpw_footer_text a:visited,
.avpw .avpw_footer_text a:hover,
.avpw .avpw_footer_text a:active {
  color: #656565;
/* tool icons */
/* normal */
.avpw .avpw_tool_icon_image {
  background-color: #efefef;
/* hover */
.avpw .avpw_tool_icon:hover .avpw_tool_icon_image {
  background-color: #ffffff;
/* pressod */
.avpw .avpw_tool_icon_down .avpw_tool_icon_image,
.avpw .avpw_tool_icon_down:hover .avpw_tool_icon_image {
  background-color: #e2e2e2;
/* tools paging inidcator */
body .avpw .avpw_page {
  color: #3d3d3d;
body .avpw .avpw_page_selected {
  color: #00a5ff;
/* default buttons */
body .avpw .avpw_button {
  background: #efefef;
/* hover */
body .avpw .avpw_button:hover {
  background: #ffffff;
/* pressod */
body .avpw .avpw_button.avpw_button_down {
  background-color: #e2e2e2;
/* primary button
 * (overriding color on these links so
 * we have to be explicit)
body .avpw .avpw_primary_button,
body .avpw .avpw_primary_button:link,
body .avpw .avpw_primary_button:visited,
body .avpw .avpw_primary_button:active {
  background-color: #0084cc;
  color: #ffffff;
/* +/- slider label/buttons */
body .avpw .avpw_slider_label {
  color: #707070;
/* darker, cut-out color */
.avpw .avpw_inset_background {
  background-color: #3e464a;
/* preset icons */
.avpw .avpw_preset_icon {
  border-left-color: #556066;
  border-right-color: #272c2e;
.avpw .avpw_preset_icon_active {
  background-color: #718087;
/* and light blue indicator */
.avpw .avpw_preset_icon_active .avpw_preset_indicator {
  background-color: #49edfc;
/* grouped push-buttons inside tools */
.avpw .avpw_inset_button {
  border-left-color: #556066;
  border-right-color: #272c2e;
/* hover */
.avpw .avpw_inset_button:hover {
  background-color: #3c4347;
/* pressod */
.avpw .avpw_inset_button_down {
  background: #272c2e;
/* text input fields */
body .avpw .avpw_text_input {
  background: #ffffff;
  color: #bcbec0;
/* focus */
body .avpw .avpw_text_input_focused {
  color: #3d3f40;
/* palette swapped on dark background */
.avpw .avpw_inset_background .avpw_text_input {
  background: #515b60;
/* focus */
.avpw .avpw_inset_background .avpw_text_input_focused {
  color: #ffffff;
/* labels palette swapped on dark background */
.avpw .avpw_inset_background .avpw_label {
  color: #ffffff;
/* frame icon */
/* 'off'/default state */
.avpw .avpw_inset_button .avpw_frame_toggle_icon {
  border-color: #717171;
/* 'on'/active state */
.avpw  .avpw_inset_button_active .avpw_frame_toggle_icon {
  border-color: #49c0fc;
/* lock icon */
/* 'unlocked'/default state */
.avpw .avpw_inset_button .avpw_lock_icon_top,
.avpw .avpw_inset_button .avpw_lock_icon_bottom {
  background: #a3a3a3;
/* 'locked'/down state */
.avpw .avpw_inset_button .avpw_lock_icon_top_inner,
.avpw .avpw_inset_button .avpw_lock_icon_sep {
  background: #383838;
/* 'locked'/active state */
.avpw .avpw_inset_button_active .avpw_lock_icon_top,
.avpw .avpw_inset_button_active .avpw_lock_icon_bottom {
  background: #49c0fc;

Getting Updates from Aviary's Development Team

If you use the widget on your site, it's very important for us to be able to contact you. Please, please, please make sure to enter a valid email at the time you generate an API key. We will use this email to inform you when we are introducing new features, making any planned layout changes, and most importantly if we need to introduce any changes to our editor that might impact your site. We will not spam you or share your email with any third party services.