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 IE7 and 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, WP7), which have a UI optimized for smaller screens and performance tuned to the native device.
Create your own with the Code Generator.
http://feather.aviary.com/js/feather.js ("feather.js")
SSL-secure page? Please use this location (via Amazon Cloudfront):
https://dme0ih8comzn4.cloudfront.net/js/feather.js
Note: Please always call this script directly, as there are some server-side dependencies.
The following describes how the editor works in your webpage in the most basic terms:
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
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.
required
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.
required
This lets Aviary know which version of the editor to load and allows you some protection from breaking changes.
The latest editor version is 2.
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):
ca Catalanzh_HANS Chinese (simplified)zh_HANT Chinese (traditional)nl Dutchfi Finnishfr Frenchde Germanhe Hebrewid Indonesianit Italianja Japaneseko Koreanlv Latvianlt Lithuanianpl Polishpt Portuguesept_BR Portuguese (Brazilian)ru Russianes Spanishsv Swedishvi VietnameseNote: You can also help us translate your own language! Shoot us an email at api@aviary.com 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.
Set to true 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.
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 (new): Autocorrect your photo with one of four basic enhancements.effects (new): Choose from a variety of effects and filters for your photo.stickers: Choose from a variety of stickers you can resize and place on your photo.orientation (new): Rotate and flip your photo in one tool.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 (new).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 (new): Blur or sharpen the overall image in one tool.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 IE7-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.
Legacy support: If you are using the legacy editor (apiVersion set to 1) the new tools are unavailable to you. However, you may find substitutes in the following deprecated tools.
rotate (deprecated): Rotate your photo in 90 degree increments.flip (deprecated): Flip and mirror your photo.colors (deprecated): Adjust the overall red, green and blue tone.blur (deprecated) : Add a blur to the overall image.sharpen (deprecated): Adjust the overall sharpness.The widget can start with a specific tool open by passing its name here.
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.
Defaults:
[
'320x240','640x480','800x600','1280x1024','1600x1200',
'240x320','480x640','600x800','1024x1280','1200x1600',
'Original',
['Square', '1:1'],
'Custom',
'3:2', '3:5', '4:3', '4:6', '5:7', '8:10', '16:9'
]
You can label the presets by providing each as an array:
[
['Photo', '4:3'],
['HDTV', '16:9'],
['Avatar', '60x50']
]
Defaults to false, but if set to true, this will disable the inverting
of crop presets (e.g. clicking "3:4" to change it to "4:3").
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.
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 a location on your server for Aviary to send a server-to-server HTTP POST to on save with information about the edited photo. A publicly accessible (and not password-protected) web service should listen here. See Saving Your Work and our examples.
Aviary will make an HTTP POST to the URL specified with the following data:
url : URL of the saved image.postdata : any additional custom data you would like sent to postUrl.Note on security - Unfortunately we cannot guarantee an IP or range of IPs to expect due to our distributed hosting. Signing could be accomplished by clever use of postData
Set data to be posted to the postUrl along with the saved file's url
(See Saving Your Work, below). Expect all data assigned to postData to be sent to your postUrl as the postdata param, which will be serialized as a string.
Set the saved file format. Accepts: png, jpg, original
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.
Pass a function to be called when the image save is complete. A This can be used to set the image in the DOM to point to the new source.
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.
Note - this image may not yet be ready so you
will have to poll this link, or alternatively handle the hi-res image
server-side at the location specified by PostUrl.
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.image param provided.url param could not be accessed by Aviary service.tools or not supported by this browser.url param so Aviary server can generate generic image data with no origin.url)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:
featherEditor.launch(overrideConfigObj);
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
featherEditor.getImageDimensions();
This will return an object with width and height properties.
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.
featherEditor.save();
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.
featherEditor.close();
<img id="editableimage1" src="http://example.com/public/images/goat.jpg"/>
<!-- Load widget code -->
<script type="text/javascript" src="http://feather.aviary.com/js/feather.js"></script>
<!-- Instantiate the widget -->
<script type="text/javascript">
var featherEditor = new Aviary.Feather({
apiKey: '1234567',
apiVersion: 2,
tools: ['draw', 'stickers'],
onSave: function(imageID, newURL) {
var img = document.getElementById(imageID);
img.src = newURL;
},
postUrl: 'http://example.com/featherposturl'
});
function launchEditor(id, src) {
featherEditor.launch({
image: id,
url: src
});
return false;
}
</script>
<!-- Add an edit button, passing the HTML id of the image
and the public URL to the image -->
<a href="#" onclick="return launchEditor('editableimage1',
'http://example.com/public/images/goat.jpg');">Edit!</a>
<!-- original line of HTML here: -->
<img id="editableimage1" src="http://example.com/public/images/goat.jpg"/>
onSaveButtonClicked, allowing an opportunity to abort the save via API.onSave, and then show a confirmation dialog to the user.postUrl with a public link to the temporary image.
postUrl service receives this information from Aviary and can save the edited images locally.#url
#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| f.read }
# get file name from URL
file_name = params[:url].split("/").pop()
# Write the file to the local filesystem
Dir.chdir("tmp")
File.open(file_name, 'w') {|f| f.write(image_from_web) }
Dir.chdir("../")
end
<?php
$image_data = file_get_contents($_REQUEST['url']);
file_put_contents("photo.jpg",$image_data);
?>
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);
}
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.
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.
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 partners@aviary.com
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).
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 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).
The minimumStyling config param can be very helpful if you plan to do heavy customization as it gives you something of a blank canvas.
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;
}
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.