diff --git src/bp-core/bp-core-attachments.php src/bp-core/bp-core-attachments.php
index e69de29..b68b037 100644
--- src/bp-core/bp-core-attachments.php
+++ src/bp-core/bp-core-attachments.php
@@ -0,0 +1,287 @@
+<?php
+/**
+ * BuddyPress Attachments functions.
+ *
+ * @package BuddyPress
+ * @subpackage Attachments
+ */
+
+// Exit if accessed directly
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Returns the directory containing the javascrit templates needed for the Uploader
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @return string the directory containing the javascrit templates
+ */
+function bp_attachments_templates() {
+	return apply_filters( 'bp_attachments_templates', trailingslashit( buddypress()->themes_dir ) . 'bp-attachments' );
+}
+
+/**
+ * Registers the Attachments templates in BuddyPress templates stack
+ *
+ * @since  BuddyPress (2.3.0)
+ */
+function bp_attachements_register_templates() {
+	bp_register_template_stack( 'bp_attachments_templates' );
+}
+add_action( 'bp_register_theme_directory', 'bp_attachements_register_templates' );
+
+/**
+ * Get the BuddyPress Plupload settings
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses  apply_filters() call 'bp_attachments_get_plupload_default_settings' to override the settings
+ * @return array list of BuddyPress Plupload settings
+ */
+function bp_attachments_get_plupload_default_settings() {
+
+	$max_upload_size = wp_max_upload_size();
+
+	if ( ! $max_upload_size ) {
+		$max_upload_size = 0;
+	}
+
+	$defaults = array(
+		'runtimes'            => 'html5,flash,silverlight,html4',
+		'file_data_name'      => 'file',
+		'multipart_params'    => array(
+			'action'          => 'bp_upload_attachment',
+			'_wpnonce'        => wp_create_nonce( 'bp-uploader' ),
+		),
+		'url'                 => admin_url( 'admin-ajax.php', 'relative' ),
+		'flash_swf_url'       => includes_url( 'js/plupload/plupload.flash.swf' ),
+		'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
+		'filters' => array(
+			'max_file_size'   => $max_upload_size . 'b',
+		),
+		'multipart'           => true,
+		'urlstream_upload'    => true,
+	);
+
+	// WordPress is not allowing multi selection for iOs 7 device.. See #29602.
+	if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false &&
+		strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) {
+
+		$defaults['multi_selection'] = false;
+	}
+
+	$settings = array(
+		'defaults' => $defaults,
+		'browser'  => array(
+			'mobile'    => wp_is_mobile(),
+			'supported' => _device_can_upload(),
+		),
+		'limitExceeded' => is_multisite() && ! is_upload_space_available(),
+	);
+
+	/**
+	 * Filter the BuddyPress Plupload default settings.
+	 *
+	 * @since 2.3.0
+	 *
+	 * @param array $params Default Plupload parameters array.
+	 */
+	return apply_filters( 'bp_attachments_get_plupload_default_settings', $settings );
+}
+
+/**
+ * Builds localization strings for the BuddyPress Uploader scripts
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses  apply_filters() call 'bp_attachments_get_plupload_l10n' to override the settings
+ * @return array Plupload default localization strings
+ */
+function bp_attachments_get_plupload_l10n() {
+	// Localization strings
+	return apply_filters( 'bp_attachments_get_plupload_l10n', array(
+			'queue_limit_exceeded'      => __( 'You have attempted to queue too many files.', 'buddypress' ),
+			'file_exceeds_size_limit'   => __( '%s exceeds the maximum upload size for this site.', 'buddypress' ),
+			'zero_byte_file'            => __( 'This file is empty. Please try another.', 'buddypress' ),
+			'invalid_filetype'          => __( 'This file type is not allowed. Please try another.', 'buddypress' ),
+			'not_an_image'              => __( 'This file is not an image. Please try another.', 'buddypress' ),
+			'image_memory_exceeded'     => __( 'Memory exceeded. Please try another smaller file.', 'buddypress' ),
+			'image_dimensions_exceeded' => __( 'This is larger than the maximum size. Please try another.', 'buddypress' ),
+			'default_error'             => __( 'An error occurred in the upload. Please try again later.', 'buddypress' ),
+			'missing_upload_url'        => __( 'There was a configuration error. Please contact the server administrator.', 'buddypress' ),
+			'upload_limit_exceeded'     => __( 'You may only upload 1 file.', 'buddypress' ),
+			'http_error'                => __( 'HTTP error.', 'buddypress' ),
+			'upload_failed'             => __( 'Upload failed.', 'buddypress' ),
+			'big_upload_failed'         => __( 'Please try uploading this file with the %1$sbrowser uploader%2$s.', 'buddypress' ),
+			'big_upload_queued'         => __( '%s exceeds the maximum upload size for the multi-file uploader when used in your browser.', 'buddypress' ),
+			'io_error'                  => __( 'IO error.', 'buddypress' ),
+			'security_error'            => __( 'Security error.', 'buddypress' ),
+			'file_cancelled'            => __( 'File canceled.', 'buddypress' ),
+			'upload_stopped'            => __( 'Upload stopped.', 'buddypress' ),
+			'dismiss'                   => __( 'Dismiss', 'buddypress' ),
+			'crunching'                 => __( 'Crunching&hellip;', 'buddypress' ),
+			'unique_file_warning'       => __( 'Make sure to upload a unique file', 'buddypress' ),
+			'error_uploading'           => __( '&#8220;%s&#8221; has failed to upload.', 'buddypress' )
+	) );
+}
+
+/**
+ * Enqueues the script needed for the Uploader UI
+ *
+ * @see  BP_Attachment::script_data() && BP_Attachment_Avatar::script_data() for examples showing how
+ * to set specific script data
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @param  string $class name of the class extending BP_Attachment (eg: BP_Attachment_Avatar)
+ */
+function bp_attachments_enqueue_scripts( $class = '' ) {
+	// Enqueue me just once per page, please.
+	if ( did_action( 'bp_attachments_enqueue_scripts' ) ) {
+		return;
+	}
+
+	if ( empty( $class ) || ! class_exists( $class ) ) {
+		return new WP_Error( 'missing_parameter' );
+	}
+
+	// Get an instance of the class and get the script data
+	$attachment = new $class;
+	$script_data  = $attachment->script_data();
+
+	$args = bp_parse_args( $script_data, array(
+		'action'            => '',
+		'file_data_name'    => '',
+		'max_file_size'     => 0,
+		'browse_button'     => 'bp-browse-button',
+		'container'         => 'bp-upload-ui',
+		'drop_element'      => 'drag-drop-area',
+		'bp_params'         => array(),
+		'extra_css'         => array(),
+		'extra_js'          => array(),
+		'feedback_messages' => array(),
+	), 'attachments_enqueue_scripts' );
+
+	if ( empty( $args['action'] ) || empty( $args['file_data_name'] ) ) {
+		return new WP_Error( 'missing_parameter' );
+	}
+
+	// Get the BuddyPress uploader strings
+	$strings = bp_attachments_get_plupload_l10n();
+
+	// Get the BuddyPress uploader settings
+	$settings = bp_attachments_get_plupload_default_settings();
+
+	// Set feedback messages
+	if ( ! empty( $args['feedback_messages'] ) ) {
+		$strings['feedback_messages'] = $args['feedback_messages'];
+	}
+
+	// Use a temporary var to ease manipulation
+	$defaults = $settings['defaults'];
+
+	// Set the upload action
+	$defaults['multipart_params']['action'] = $args['action'];
+
+	// Set BuddyPress upload parameters if provided
+	if ( ! empty( $args['bp_params'] ) ) {
+		$defaults['multipart_params']['bp_params'] = $args['bp_params'];
+	}
+
+	// Merge other arguments
+	$ui_args = array_intersect_key( $args, array(
+		'file_data_name' => true,
+		'browse_button'  => true,
+		'container'      => true,
+		'drop_element'   => true,
+	) );
+
+	$defaults = array_merge( $defaults, $ui_args );
+
+	if ( ! empty( $args['max_file_size'] ) ) {
+		$defaults['filters']['max_file_size'] = $args['max_file_size'] . 'b';
+	}
+
+	// Specific to BuddyPress Avatars
+	if ( 'bp_avatar_upload' == $defaults['multipart_params']['action'] ) {
+
+		// Include the cropping informations for avatars
+		$settings['crop'] = array(
+			'full_h'  => bp_core_avatar_full_height(),
+			'full_w'  => bp_core_avatar_full_width(),
+		);
+
+		// Avatar only need 1 file and 1 only!
+		$defaults['multi_selection'] = false;
+
+		// Init the Avatar nav
+		$avatar_nav = array( 'upload' => array( 'id' => 'upload', 'caption' => __( 'Upload', 'buddypress' ), 'order' => 0  ) );
+
+		// Use this filter to disable the Webcam Avatar feature
+		if ( false !== apply_filters( 'bp_attachment_avatar_use_webcam', true ) && 'user' == $defaults['multipart_params']['bp_params']['object'] ) {
+			$avatar_nav['camera'] = array( 'id' => 'camera', 'caption' => __( 'Camera', 'buddypress' ), 'order' => 10 );
+
+			// Set warning messages
+			$strings['camera_warnings'] = array(
+				'requesting'  => __( 'Requesting video stream, please authorize this website to access to your camera.', 'buddypress'),
+				'loading'     => __( 'Please wait for the video to load.', 'buddypress' ),
+				'loaded'      => __( 'Video stream loaded. You can use the capture button to display the avatar preview.', 'buddypress' ),
+				'noaccess'    => __( 'You denied this website to access to your camera. Please use the upload form.', 'buddypress' ),
+				'errormsg'    => __( 'Your browser is not supported. Please use the upload form.', 'buddypress' ),
+				'videoerror'  => __( 'Video error. Please use the upload form.', 'buddypress' ),
+				'ready'       => __( 'Avatar ready, use the save button to validate.', 'buddypress' ),
+				'nocapture'   => __( 'No avatar was captured, please use the capture button first.', 'buddypress' ),
+			);
+		}
+
+		if ( ! empty( $defaults['multipart_params']['bp_params']['has_avatar'] ) ) {
+			$avatar_nav['delete'] = array( 'id' => 'delete', 'caption' => __( 'Delete', 'buddypress' ), 'order' => 100  );
+		}
+
+		$settings['nav'] = bp_sort_by_key( apply_filters( 'bp_attachments_avatar_nav', $avatar_nav ), 'order', 'num' );
+	}
+
+	// Set Plupload settings
+	$settings['defaults'] = $defaults;
+
+	/**
+	 * Enqueue some extra styles if required
+	 *
+	 * Extra styles need to be registered.
+	 */
+	if ( ! empty( $args['extra_css'] ) ) {
+		foreach ( (array) $args['extra_css'] as $css ) {
+			if ( empty( $css ) ) {
+				continue;
+			}
+
+			wp_enqueue_style( $css );
+		}
+	}
+
+	wp_enqueue_script ( 'bp-plupload' );
+	wp_localize_script( 'bp-plupload', 'BP_Uploader', array( 'strings' => $strings, 'settings' => $settings ) );
+
+	/**
+	 * Enqueue some extra scripts if required
+	 *
+	 * Extra scripts need to be registered.
+	 */
+	if ( ! empty( $args['extra_js'] ) ) {
+		foreach ( (array) $args['extra_js'] as $js ) {
+			if ( empty( $js ) ) {
+				continue;
+			}
+
+			wp_enqueue_script( $js );
+		}
+	}
+
+	/**
+	 * Fires at the conclusion of bp_attachments_enqueue_scripts()
+	 * to avoid the scripts to be loaded more than once.
+	 *
+	 * @since BuddyPress 2.3.0
+	 */
+	do_action( 'bp_attachments_enqueue_scripts' );
+}
diff --git src/bp-core/bp-core-avatars.php src/bp-core/bp-core-avatars.php
index 44fe3c3..d58f805 100644
--- src/bp-core/bp-core-avatars.php
+++ src/bp-core/bp-core-avatars.php
@@ -553,6 +553,62 @@ function bp_core_delete_existing_avatar( $args = '' ) {
 }
 
 /**
+ * Ajax delete an avatar for a given object and item id
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses    bp_core_delete_existing_avatar()
+ * @return  string a json object containing success data if the avatar was deleted
+ *                 error message otherwise
+ */
+function bp_core_avatar_ajax_delete() {
+	// Bail if not a POST action
+	if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
+		wp_send_json_error();
+	}
+
+	$avatar_data = $_POST;
+
+	if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) ) {
+		wp_send_json_error();
+	}
+
+	$nonce = 'bp_delete_avatar_link';
+	if ( 'group' == $avatar_data['object'] ) {
+		$nonce = 'bp_group_avatar_delete';
+	}
+
+	// Check the nonce
+	check_admin_referer( $nonce, 'nonce' );
+
+	// Capability check
+	if ( ! bp_is_item_admin() && ! bp_current_user_can( 'bp_moderate' ) ) {
+		wp_send_json_error();
+	}
+
+	// Handle delete
+	if ( bp_core_delete_existing_avatar( array( 'item_id' => $avatar_data['item_id'], 'object' => $avatar_data['object'] ) ) ) {
+		$return = array(
+			'avatar' => bp_core_fetch_avatar( array(
+				'object'  => $avatar_data['object'],
+				'item_id' => $avatar_data['item_id'],
+				'html'    => false,
+				'type'    => 'full',
+			) ),
+			'feedback_code' => 4,
+			'item_id'       => $avatar_data['item_id'],
+		);
+
+		wp_send_json_success( $return );
+	} else {
+		wp_send_json_error( array(
+			'feedback_code' => 3,
+		) );
+	}
+}
+add_action( 'wp_ajax_bp_avatar_delete', 'bp_core_avatar_ajax_delete' );
+
+/**
  * Handle avatar uploading.
  *
  * The functions starts off by checking that the file has been uploaded
@@ -625,6 +681,183 @@ function bp_core_avatar_handle_upload( $file, $upload_dir_filter ) {
 }
 
 /**
+ * Ajax upload an avatar
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @uses  bp_core_avatar_handle_upload()
+ * @uses  apply_filters() call 'bp_core_avatar_ajax_upload_params' if you need to ajax set
+ *                        avatars for another component (eg: blavatar)
+ * @return  string a json object containing success data if the upload succeeded
+ *                 error message otherwise
+ */
+function bp_core_avatar_ajax_upload() {
+	// Bail if not a POST action
+	if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
+		wp_send_json_error();
+	}
+
+	// Check the nonce
+	check_admin_referer( 'bp-uploader' );
+
+	// Capability check
+	if ( ! bp_is_item_admin() && ! bp_current_user_can( 'bp_moderate' ) && ! bp_is_group_create() ) {
+		wp_send_json_error();
+	}
+
+	// Init the BuddyPress parameters
+	$bp_params = array();
+
+	// We need it to carry on
+	if ( ! empty( $_POST['bp_params' ] ) ) {
+		$bp_params = $_POST['bp_params' ];
+	} else {
+		wp_send_json_error();
+	}
+
+	// We need the object to set the uploads dir filter
+	if ( empty( $bp_params['object'] ) ) {
+		wp_send_json_error();
+	}
+
+	$bp = buddypress();
+	$bp_params['upload_dir_filter'] = '';
+	$needs_reset = array();
+
+	if ( 'user' == $bp_params['object'] && bp_is_active( 'xprofile' ) ) {
+		$bp_params['upload_dir_filter'] = 'xprofile_avatar_upload_dir';
+
+		if ( ! bp_displayed_user_id() && ! empty( $bp_params['item_id'] ) ) {
+			$needs_reset = array( 'key' => 'displayed_user', 'value' => $bp->displayed_user );
+			$bp->displayed_user->id = $bp_params['item_id'];
+		}
+	} else if ( 'group' == $bp_params['object'] && bp_is_active( 'groups' ) ) {
+		$bp_params['upload_dir_filter'] = 'groups_avatar_upload_dir';
+
+		if ( ! bp_get_current_group_id() && ! empty( $bp_params['item_id'] ) ) {
+			$needs_reset = array( 'component' => 'groups', 'key' => 'current_group', 'value' => $bp->groups->current_group );
+			$bp->groups->current_group = groups_get_group( array(
+				'group_id'        => $bp_params['item_id'],
+				'populate_extras' => false,
+			) );
+		}
+	} else {
+		/**
+		 * Filter here to deal with other components
+		 *
+		 * @since BuddyPress (2.3.0)
+		 *
+		 * @var array $bp_params the BuddyPress Ajax parameters
+		 */
+		$bp_params = apply_filters( 'bp_core_avatar_ajax_upload_params', $bp_params );
+	}
+
+	if ( ! isset( $bp->avatar_admin ) ) {
+		$bp->avatar_admin = new stdClass();
+	}
+
+	// Upload the avatar
+	$avatar = bp_core_avatar_handle_upload( $_FILES, $bp_params['upload_dir_filter'] );
+
+	// Reset objects
+	if ( ! empty( $needs_reset ) ) {
+		if ( ! empty( $needs_reset['component'] ) ) {
+			$bp->{$needs_reset['component']}->{$needs_reset['key']} = $needs_reset['value'];
+		} else {
+			$bp->{$needs_reset['key']} = $needs_reset['value'];
+		}
+	}
+
+	// Intercept the template message and remove it
+	if ( empty( $avatar ) ) {
+		$message = $bp->template_message;
+
+		// Remove template message.
+		$bp->template_message      = false;
+		$bp->template_message_type = false;
+		@setcookie( 'bp-message', false, time() - 1000, COOKIEPATH );
+		@setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH );
+
+		wp_send_json_error(array(
+			'type' => 'upload_error',
+			'message' => $message,
+		) );
+	}
+
+	if ( empty( $bp->avatar_admin->image->file ) ) {
+		wp_send_json_error();
+	}
+
+	$uploaded_image = @getimagesize( $bp->avatar_admin->image->file );
+
+	// Set the name of the file
+	$name = $_FILES['file']['name'];
+	$name_parts = pathinfo( $name );
+	$name = trim( substr( $name, 0, - ( 1 + strlen( $name_parts['extension'] ) ) ) );
+
+	if ( 'user' == $bp_params['object'] ) {
+		do_action( 'xprofile_avatar_uploaded' );
+	}
+
+	// Finally return the avatar to the editor
+	wp_send_json_success( array(
+		'name'      => $name,
+		'url'       => $bp->avatar_admin->image->url,
+		'width'     => $uploaded_image[0],
+		'height'    => $uploaded_image[1],
+	) );
+}
+add_action( 'wp_ajax_bp_avatar_upload', 'bp_core_avatar_ajax_upload' );
+
+ /**
+ * Handle avatar webcam capture.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param string $data base64 encoded image.
+ * @param int $item_id.
+ * @return bool True on success, false on failure.
+ */
+function bp_core_avatar_handle_capture( $data = '', $item_id = 0 ) {
+	if ( empty( $data ) || empty( $item_id ) ) {
+		return false;
+	}
+
+	$avatar_dir = bp_core_avatar_upload_path() . '/avatars';
+
+	// It's not a regular upload, we may need to create this folder
+	if ( ! file_exists( $avatar_dir ) ) {
+		if ( ! wp_mkdir_p( $avatar_dir ) ) {
+			return false;
+		}
+	}
+
+	$avatar_folder_dir = apply_filters( 'bp_core_avatar_folder_dir', $avatar_dir . '/' . $item_id, $item_id, 'user', 'avatars' );
+
+	// It's not a regular upload, we may need to create this folder
+	if( ! file_exists( $avatar_folder_dir ) ) {
+		if ( ! wp_mkdir_p( $avatar_folder_dir ) ) {
+			return false;
+		}
+	}
+
+	$original_file = $avatar_folder_dir . '/webcam-capture-' . $item_id . '.png';
+
+	if ( file_put_contents( $original_file, $data ) ) {
+		$avatar_to_crop = str_replace( bp_core_avatar_upload_path(), '', $original_file );
+
+		// Crop to default values
+		$crop_args = array( 'item_id' => $item_id, 'original_file' => $avatar_to_crop, 'crop_x' => 0, 'crop_y' => 0 );
+
+		do_action( 'xprofile_avatar_uploaded' );
+
+		return bp_core_avatar_handle_crop( $crop_args );
+	} else {
+		return false;
+	}
+}
+
+/**
  * Crop an uploaded avatar.
  *
  * $args has the following parameters:
@@ -689,6 +922,123 @@ function bp_core_avatar_handle_crop( $args = '' ) {
 }
 
 /**
+ * Ajax set an avatar for a given object and item id
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @uses  bp_core_avatar_handle_capture()
+ * @uses  bp_core_avatar_handle_crop()
+ * @return  string a json object containing success data if the crop/capture succeeded
+ *                 error message otherwise
+ */
+function bp_core_avatar_ajax_set() {
+	// Bail if not a POST action
+	if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
+		wp_send_json_error();
+	}
+
+	// Check the nonce
+	check_admin_referer( 'bp_avatar_cropstore', 'nonce' );
+
+	// Capability check
+	if ( ! bp_is_item_admin() && ! bp_current_user_can( 'bp_moderate' ) && ! bp_is_group_create() ) {
+		wp_send_json_error();
+	}
+
+	$avatar_data = wp_parse_args( $_POST, array(
+		'crop_w' => bp_core_avatar_full_width(),
+		'crop_h' => bp_core_avatar_full_height(),
+		'crop_x' => 0,
+		'crop_y' => 0
+	) );
+
+	if ( empty( $avatar_data['object'] ) || empty( $avatar_data['item_id'] ) || empty( $avatar_data['original_file'] ) ) {
+		wp_send_json_error();
+	}
+
+	if ( ! empty( $avatar_data['type'] ) && 'camera' == $avatar_data['type'] && 'user' == $avatar_data['object'] ) {
+		$webcam_avatar = false;
+
+		if ( ! empty( $avatar_data['original_file'] ) ) {
+			$webcam_avatar = str_replace( array( 'data:image/png;base64,', ' ' ), array( '', '+' ), $avatar_data['original_file'] );
+			$webcam_avatar = base64_decode( $webcam_avatar );
+		}
+
+		if ( ! bp_core_avatar_handle_capture( $webcam_avatar, $avatar_data['item_id'] ) ) {
+			wp_send_json_error( array(
+				'feedback_code' => 1
+			) );
+
+		} else {
+			$return = array(
+				'avatar' => bp_core_fetch_avatar( array(
+					'object'  => $avatar_data['object'],
+					'item_id' => $avatar_data['item_id'],
+					'html'    => false,
+					'type'    => 'full',
+				) ),
+				'feedback_code' => 2,
+				'item_id'       => $avatar_data['item_id'],
+			);
+
+			do_action( 'xprofile_screen_change_avatar' );
+
+			wp_send_json_success( $return );
+		}
+
+		return;
+	}
+
+	$original_file = str_replace( bp_core_avatar_url(), '', $avatar_data['original_file'] );
+
+	// Set avatars dir & feedback part
+	if ( 'user' == $avatar_data['object'] ) {
+		$avatar_dir = 'avatars';
+
+	// Defaults to object-avatars dir
+	} else {
+		$avatar_dir = sanitize_key( $avatar_data['object'] ) . '-avatars';
+	}
+
+	// Crop args
+	$r = array(
+		'item_id'       => $avatar_data['item_id'],
+		'object'        => $avatar_data['object'],
+		'avatar_dir'    => $avatar_dir,
+		'original_file' => $original_file,
+		'crop_w'        => $avatar_data['crop_w'],
+		'crop_h'        => $avatar_data['crop_h'],
+		'crop_x'        => $avatar_data['crop_x'],
+		'crop_y'        => $avatar_data['crop_y']
+	);
+
+	// Handle crop
+	if ( bp_core_avatar_handle_crop( $r ) ) {
+		$return = array(
+			'avatar' => bp_core_fetch_avatar( array(
+				'object'  => $avatar_data['object'],
+				'item_id' => $avatar_data['item_id'],
+				'html'    => false,
+				'type'    => 'full',
+			) ),
+			'feedback_code' => 2,
+			'item_id'       => $avatar_data['item_id'],
+		);
+
+		if ( 'user' == $avatar_data['object'] ) {
+			do_action( 'xprofile_screen_change_avatar' );
+		}
+
+		wp_send_json_success( $return );
+	} else {
+		wp_send_json_error( array(
+			'feedback_code' => 1,
+		) );
+	}
+}
+add_action( 'wp_ajax_bp_avatar_set', 'bp_core_avatar_ajax_set' );
+
+/**
  * Replace default WordPress avatars with BP avatars, if available.
  *
  * Filters 'get_avatar'.
@@ -1098,3 +1448,87 @@ function bp_core_avatar_reset_query( $posts_query = null ) {
 	}
 }
 add_action( 'bp_parse_query', 'bp_core_avatar_reset_query', 10, 1 );
+
+/**
+ * Checks whether Avatar UI should be loaded
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses   bp_is_user_change_avatar()
+ * @uses   bp_get_avatar_admin_step()
+ * @uses   bp_is_active()
+ * @uses   bp_is_group_create()
+ * @uses   bp_is_group_creation_step()
+ * @uses   bp_is_group_admin_page()
+ * @uses   bp_is_group_admin_screen()
+ * @return bool True if Avatar UI should load, false otherwise
+ */
+function bp_core_avatar_is_front_edit_avatar() {
+	$retval = false;
+
+	if ( bp_is_user_change_avatar() && 'crop-image' != bp_get_avatar_admin_step() ) {
+		$retval = true;
+	}
+
+	if ( bp_is_active( 'groups' ) ) {
+		// Group creation
+		if ( bp_is_group_create() && bp_is_group_creation_step( 'group-avatar' ) && 'crop-image' != bp_get_avatar_admin_step() ) {
+			$retval = true;
+
+		// Group Manage
+		} else if ( bp_is_group_admin_page() && bp_is_group_admin_screen( 'group-avatar' ) && 'crop-image' != bp_get_avatar_admin_step() ) {
+			$retval = true;
+		}
+	}
+
+	/**
+	 * Use this filter if you need to :
+	 * - Load the avatar UI for a component that is !groups or !user (return true regarding your conditions)
+	 * - Completely disable the avatar UI introduced in 2.3 (eg: __return_false())
+	 *
+	 * @since  BuddyPress (2.3.0)
+	 *
+	 * @var  bool whether to load the Avatar UI
+	 */
+	return apply_filters( 'bp_core_avatar_is_front_edit_avatar', $retval );
+}
+
+/**
+ * Template function to load the Avatar UI javascript templates
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses  bp_core_avatar_is_front_edit_avatar() to check whether javascript templates should be loaded
+ * @uses  bp_get_template_part() to load the template part
+ */
+function bp_core_avatar_get_template_part() {
+	if ( ! bp_core_avatar_is_front_edit_avatar() ) {
+		return;
+	}
+
+	bp_get_template_part( 'avatars/index' );
+}
+
+/**
+ * Trick to check if the theme's BuddyPress templates are up to date
+ *
+ * If the "avatar templates" are not including the new template tag, this will
+ * help users to get the avatar UI and inform the most curious that their
+ * templates are out of date.
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses  bp_core_avatar_is_front_edit_avatar() to check whether javascript templates should be loaded
+ * @uses  did_action() to check if the javascript template was loaded
+ * @uses  bp_get_template_part() to load the template part
+ */
+function bp_core_avatar_template_check() {
+	if ( ! bp_core_avatar_is_front_edit_avatar() ) {
+		return;
+	}
+
+	if ( ! did_action( 'bp_attachments_avatar_check_template' ) ) {
+		_doing_it_wrong( 'Theme Compatibility', __( "The templates of your theme should be updated", 'buddypress' ), '2.3' );
+		bp_get_template_part( 'avatars/index' );
+	}
+}
diff --git src/bp-core/bp-core-cssjs.php src/bp-core/bp-core-cssjs.php
index e97748b..79bae82 100644
--- src/bp-core/bp-core-cssjs.php
+++ src/bp-core/bp-core-cssjs.php
@@ -28,20 +28,26 @@ function bp_core_register_common_scripts() {
 	$scripts = apply_filters( 'bp_core_register_common_scripts', array(
 
 		// Legacy
-		'bp-confirm'        => array( 'file' => "{$url}confirm{$min}.js",        'dependencies' => array( 'jquery' ) ),
-		'bp-widget-members' => array( 'file' => "{$url}widget-members{$min}.js", 'dependencies' => array( 'jquery' ) ),
-		'bp-jquery-query'   => array( 'file' => "{$url}jquery-query{$min}.js",   'dependencies' => array( 'jquery' ) ),
-		'bp-jquery-cookie'  => array( 'file' => "{$url}jquery-cookie{$min}.js",  'dependencies' => array( 'jquery' ) ),
-		'bp-jquery-scroll-to' => array( 'file' => "{$url}jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ) ),
+		'bp-confirm'        => array( 'file' => "{$url}confirm{$min}.js",        'dependencies' => array( 'jquery' ), 'footer' => false ),
+		'bp-widget-members' => array( 'file' => "{$url}widget-members{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
+		'bp-jquery-query'   => array( 'file' => "{$url}jquery-query{$min}.js",   'dependencies' => array( 'jquery' ), 'footer' => false ),
+		'bp-jquery-cookie'  => array( 'file' => "{$url}jquery-cookie{$min}.js",  'dependencies' => array( 'jquery' ), 'footer' => false ),
+		'bp-jquery-scroll-to' => array( 'file' => "{$url}jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
 
 		// 2.1
-		'jquery-caret' => array( 'file' => "{$url}jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ) ),
-		'jquery-atwho' => array( 'file' => "{$url}jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ) ),
+		'jquery-caret' => array( 'file' => "{$url}jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ),
+		'jquery-atwho' => array( 'file' => "{$url}jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ), 'footer' => false ),
+
+		// 2.3
+		'bp-plupload' => array( 'file' => "{$url}bp-plupload{$min}.js", 'dependencies' => array( 'plupload', 'jquery', 'json2', 'wp-backbone' ), 'footer' => true ),
+		'bp-avatar'   => array( 'file' => "{$url}avatar{$min}.js", 'dependencies' => array( 'imgareaselect' ), 'footer' => true ),
+		'bp-webcam'   => array( 'file' => "{$url}webcam{$min}.js", 'dependencies' => array( 'bp-avatar' ), 'footer' => true ),
+
 	) );
 
 	$version = bp_get_version();
 	foreach ( $scripts as $id => $script ) {
-		wp_register_script( $id, $script['file'], $script['dependencies'], $version );
+		wp_register_script( $id, $script['file'], $script['dependencies'], $version, $script['footer'] );
 	}
 }
 add_action( 'bp_enqueue_scripts',       'bp_core_register_common_scripts', 1 );
@@ -76,7 +82,11 @@ function bp_core_register_common_styles() {
 		'bp-admin-bar' => array(
 			'file'         => $admin_bar_file,
 			'dependencies' => array( 'admin-bar' )
-		)
+		),
+		'bp-avatar' => array(
+			'file'         => "{$url}avatar{$min}.css",
+			'dependencies' => array( 'imgareaselect' )
+		),
 	) );
 
 	foreach ( $styles as $id => $style ) {
@@ -110,6 +120,30 @@ add_action( 'bp_enqueue_scripts',       'bp_core_confirmation_js' );
 add_action( 'bp_admin_enqueue_scripts', 'bp_core_confirmation_js' );
 
 /**
+ * Enqueues the css and js required by the Avatar UI
+ *
+ * @since  BuddyPress (2.3.0)
+ *
+ * @uses bp_core_avatar_is_front_edit_avatar() to check if the Avatar scripts should be loaded
+ * @uses bp_attachments_enqueue_scripts() to enqueue the css and js required by the Avatar UI
+ * @uses add_action() to eventually load the javascript template files if templates are outdated
+ */
+function bp_core_avatar_scripts() {
+	if ( ! bp_core_avatar_is_front_edit_avatar() ) {
+		return false;
+	}
+
+	// Enqueue the Attachments scripts for the Avatar UI
+	bp_attachments_enqueue_scripts( 'BP_Attachment_Avatar' );
+
+	// Add Some actions for Theme backcompat
+	add_action( 'bp_after_profile_avatar_upload_content', 'bp_core_avatar_template_check' );
+	add_action( 'bp_after_group_admin_content',           'bp_core_avatar_template_check' );
+	add_action( 'bp_after_group_avatar_creation_step',    'bp_core_avatar_template_check' );
+}
+add_action( 'bp_enqueue_scripts', 'bp_core_avatar_scripts' );
+
+/**
  * Enqueues jCrop library and hooks BP's custom cropper JS.
  */
 function bp_core_add_jquery_cropper() {
diff --git src/bp-core/classes/class-bp-attachment-avatar.php src/bp-core/classes/class-bp-attachment-avatar.php
index 8940954..66d2b75 100644
--- src/bp-core/classes/class-bp-attachment-avatar.php
+++ src/bp-core/classes/class-bp-attachment-avatar.php
@@ -252,4 +252,73 @@ class BP_Attachment_Avatar extends BP_Attachment {
 		// Return the full and thumb cropped avatars
 		return $avatar_types;
 	}
+
+	/**
+	 * Build script datas for the Uploader UI
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @uses BP_Attachment::script_data()
+	 */
+	public function script_data() {
+		// Get default script data
+		$script_data = parent::script_data();
+
+		// Defaults to Avatar Backbone script
+		$js_scripts = array( 'bp-avatar' );
+
+		if ( bp_is_user() ) {
+			// Use this filter to disable the Webcam Avatar feature
+			if ( false !== apply_filters( 'bp_attachment_avatar_use_webcam', true ) ) {
+				$js_scripts = array( 'bp-webcam' );
+			}
+
+			$script_data['bp_params'] = array(
+				'object'     => 'user',
+				'item_id'    => bp_displayed_user_id(),
+				'has_avatar' => bp_get_user_has_avatar(),
+				'nonces'  => array(
+					'set'    => wp_create_nonce( 'bp_avatar_cropstore' ),
+					'delete' => wp_create_nonce( 'bp_delete_avatar_link' ),
+				),
+			);
+
+			// Set feedback messages
+			$script_data['feedback_messages'] = array(
+				1 => __( 'There was a problem cropping your profile photo.', 'buddypress' ),
+				2 => __( 'Your new profile photo was uploaded successfully.', 'buddypress' ),
+				3 => __( 'There was a problem deleting your profile photo. Please try again.', 'buddypress'),
+				4 => __( 'Your profile photo was deleted successfully!', 'buddypress'),
+			);
+		} else if ( bp_is_group() ) {
+			$script_data['bp_params'] = array(
+				'object'     => 'group',
+				'item_id'    => bp_get_current_group_id(),
+				'has_avatar' => bp_get_group_has_avatar(),
+				'nonces'     => array(
+					'set'    => wp_create_nonce( 'bp_avatar_cropstore' ),
+					'delete' => wp_create_nonce( 'bp_group_avatar_delete' ),
+				),
+			);
+
+			// Set feedback messages
+			$script_data['feedback_messages'] = array(
+				1 => __( 'There was a problem cropping the group profile photo.', 'buddypress' ),
+				2 => __( 'The group profile photo was uploaded successfully.', 'buddypress' ),
+				3 => __( 'There was a problem deleting the group profile photo. Please try again.', 'buddypress'),
+				4 => __( 'The group profile photo was deleted successfully!', 'buddypress'),
+			);
+		} else {
+			// Blavatar ?
+			$script_data['bp_params'] = apply_filters( 'bp_attachment_avatar_params', array() );
+		}
+
+		// Include the specific css
+		$script_data['extra_css'] = array( 'bp-avatar' );
+
+		// Include the specific css
+		$script_data['extra_js']  = $js_scripts;
+
+		return $script_data;
+	}
 }
diff --git src/bp-core/classes/class-bp-attachment.php src/bp-core/classes/class-bp-attachment.php
index 337c27f..8e63e0b 100644
--- src/bp-core/classes/class-bp-attachment.php
+++ src/bp-core/classes/class-bp-attachment.php
@@ -505,4 +505,25 @@ class BP_Attachment {
 		// Finally crop the image
 		return wp_crop_image( $r['original_file'], (int) $r['crop_x'], (int) $r['crop_y'], (int) $r['crop_w'], (int) $r['crop_h'], (int) $r['dst_w'], (int) $r['dst_h'], $r['src_abs'], $r['dst_file'] );
 	}
+
+	/**
+	 * Build script datas for the Uploader UI
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * Override this method from your child class to build the script datas
+	 */
+	public function script_data() {
+		$script_data = array(
+			'action'            => $this->action,
+			'file_data_name'    => $this->file_input,
+			'max_file_size'     => $this->original_max_filesize,
+			'feedback_messages' => array(
+				1 => __( 'Sorry, uploading the file failed.', 'buddypress' ),
+				2 => __( 'File successfully uploaded.', 'buddypress' ),
+			),
+		);
+
+		return $script_data;
+	}
 }
diff --git src/bp-core/css/avatar.css src/bp-core/css/avatar.css
index e69de29..f6df513 100644
--- src/bp-core/css/avatar.css
+++ src/bp-core/css/avatar.css
@@ -0,0 +1,150 @@
+div.bp-avatar-status {
+	clear:both;
+	margin:1em 0;
+}
+
+div.bp-avatar-status p.updated {
+	display: block;
+	padding: 10px 15px;
+}
+
+div.bp-avatar-status p.success {
+	background-color: #efc;
+	border: 1px solid #591;
+	color: #250;
+}
+
+div.bp-avatar-status p.error {
+	background-color: #fdc;
+	border: 1px solid #a00;
+	color: #800;
+}
+
+.progress {
+	float: right;
+	height: 22px;
+	margin: 6px 10px 0 0;
+	width: 200px;
+	line-height: 2em;
+	padding: 0;
+	overflow: hidden;
+	margin-bottom: 2px;
+	border: 1px solid #d1d1d1;
+	background: none;
+}
+
+.bar {
+	z-index: 9;
+	width: 0;
+	height: 100%;
+	background-color: #c3ff88;
+}
+
+.bp-uploader-progress div.error {
+	font-size: 90%;
+	display: block;
+	padding: 10px 15px;
+	background-color: #fdc;
+	border: 1px solid #a00;
+	color: #800;
+}
+
+#bp-uploader-warning, #bp-webcam-message p.warning {
+	margin:1em 0;
+	font-size: 90%;
+	display: block;
+	padding: 10px 15px;
+	background-color: #ffec8b;
+	border: 1px solid #fc0;
+	color: #440;
+}
+
+div.bp-avatar-nav {
+	clear:both;
+	background: transparent;
+	margin: 10px 0 10px;
+	overflow: hidden;
+}
+
+.avatar-nav-items {
+	margin: 0;
+	padding: 0;
+}
+
+#buddypress .avatar-nav-items li.avatar-nav-item {
+ 	float: left;
+ 	margin: 0;
+ 	list-style: none;
+ }
+
+.avatar-nav-items li a {
+  	display: block;
+  	padding: 5px 10px;
+  	text-decoration: none;
+}
+
+.avatar-nav-items li.current a {
+	background-color: #eee;
+	color: #555;
+	opacity: .8;
+	font-weight: bold;
+}
+
+#drag-drop-area {
+	border: 4px dashed #bbb;
+	height: 200px;
+}
+
+.drag-drop.drag-over #drag-drop-area {
+	border-color: #83b4d8;
+}
+
+.drag-drop .drag-drop-inside {
+	margin: 70px auto 0;
+	width: 250px;
+}
+
+.drag-drop .drag-drop-inside p, .drag-drop-inside p.drag-drop-buttons {
+	display: block;
+}
+
+.drag-drop .drag-drop-inside p {
+	text-align: center;
+	color: #aaa;
+	font-size: 110%;
+	margin: 5px 0;
+}
+
+#avatar-to-crop {
+	float: left;
+	margin: 0 20px 20px 0;
+	text-align: left;
+}
+#avatar-crop-pane {
+	width: 150px;
+	height: 150px;
+	overflow: hidden;
+}
+
+#avatar-crop-actions {
+	margin: 20px 0;
+}
+
+#avatar-to-crop img,
+#avatar-crop-pane img,
+#avatar-crop-pane canvas,
+#avatar-upload-form img,
+#create-group-form img,
+#group-settings-form img {
+	border: none !important;
+	max-width: none !important;
+}
+
+#bp-webcam-avatar video {
+	float:left;
+	width:450px;
+}
+
+#bp-webcam-avatar #avatar-crop-pane {
+	border: 2px dashed #bbb;
+}
diff --git src/bp-core/js/avatar.js src/bp-core/js/avatar.js
index e69de29..2b10b01 100644
--- src/bp-core/js/avatar.js
+++ src/bp-core/js/avatar.js
@@ -0,0 +1,508 @@
+window.bp = window.bp || {};
+
+( function( exports, $ ) {
+
+	// Bail if not set
+	if ( typeof BP_Uploader === 'undefined' ) {
+		return;
+	}
+
+	bp.Models      = bp.Models || {};
+	bp.Collections = bp.Collections || {};
+	bp.Views       = bp.Views || {};
+
+	bp.Avatar = {
+		start: function() {
+			/**
+			 * Remove the bp-legacy UI
+			 *
+			 * If for some reason javascript is disabled
+			 * or broken, this is a way to keep the legacy UI
+			 * as nothing will happen..
+			 */
+			// User
+			if ( $( '#avatar-upload-form' ).length ) {
+				$( '#avatar-upload' ).remove();
+				$( '#avatar-upload-form p' ).remove();
+
+			// Group Manage
+			} else if ( $( '#group-settings-form' ).length ) {
+				$( '#group-settings-form p' ).each( function( i ) {
+					if ( 0 !== i ) {
+						$( this ).remove();
+					}
+				} );
+
+				if ( $( '#delete-group-avatar-button' ).length ) {
+					$( '#delete-group-avatar-button' ).remove();
+				}
+
+			// Group Create
+			} else if ( $( '#group-create-body' ).length ) {
+				$( '.main-column p #file' ).remove();
+				$( '.main-column p #upload' ).remove();
+			}
+
+			// Init some vars
+			this.views      = new Backbone.Collection();
+			this.activeView = 'upload';
+			this.iasapi     = {};
+
+			// Set up nav
+			this.setupNav();
+
+			// Create the uploader view by default
+			this.uploaderView();
+
+			// Avatars are uploaded files
+			this.avatars = bp.Uploader.filesUploaded;
+			this.avatars.on( 'add', this.cropView, this );
+		},
+
+		setView: function( view ) {
+			// Clear views
+			if ( ! _.isUndefined( this.views.models ) ) {
+				_.each( this.views.models, function( model ) {
+					model.get( 'view' ).remove();
+				}, this );
+			}
+
+			// Reset objects
+			this.views.reset();
+			this.avatars.reset();
+
+			if ( ! _.isEmpty( this.iasapi ) ) {
+				this.iasapi.remove();
+				this.iasapi = {};
+			}
+
+			// Load the required view
+			switch ( view ) {
+				case 'upload':
+					this.uploaderView();
+					break;
+
+				case 'delete':
+					this.deleteView();
+					break;
+			}
+		},
+
+		setupNav: function() {
+			var nav = new Backbone.Collection(),
+			    self = this;
+
+			_.each( BP_Uploader.settings.nav, function( item ) {
+				if ( ! _.isObject( item ) ) {
+					return;
+				}
+
+				nav.add( {
+					id: item.id,
+					name: item.caption,
+					href: '#',
+					active: ( self.activeView == item.id ) ? 1 : 0,
+				} );
+			} );
+
+			this.nav = new bp.Views.Nav( { collection: nav } );
+			this.nav.inject( '.bp-avatar-nav' );
+
+			// Listen to nav changes (it's like a do_action!)
+			this.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
+		},
+
+		uploaderView: function() {
+			// Listen to the Queued uploads
+			bp.Uploader.filesQueue.on( 'add', this.uploadProgress, this );
+
+			// Create the BuddyPress Uploader
+			uploader = new bp.Views.Uploader();
+
+			// Add it to views
+			this.views.add( { id: 'upload', view: uploader } );
+
+			// Display it
+			uploader.inject( '.bp-avatar' );
+		},
+
+		uploadProgress: function( model ) {
+			// Create the Uploader status view
+			avatarStatus = new bp.Views.uploaderStatus( { collection: bp.Uploader.filesQueue } );
+
+			if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
+				this.views.set( { id: 'status', view: avatarStatus } );
+			} else {
+				this.views.add( { id: 'status', view: avatarStatus } );
+			}
+
+			// Display it
+	 		avatarStatus.inject( '.bp-avatar-status' );
+		},
+
+		cropView: function() {
+			var status;
+
+			// Make sure to remove the uploads status
+			if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
+				status = this.views.get( 'status' );
+				status.get( 'view' ).remove();
+				this.views.remove( { id: 'status', view: status } );
+			}
+
+			// Create the Avatars view
+			avatar = new bp.Views.Avatars( { collection: this.avatars } );
+			this.views.add( { id: 'crop', view: avatar } );
+
+			avatar.inject( '.bp-avatar' );
+		},
+
+		setAvatar: function( avatar ) {
+			var self = this,
+				crop;
+
+			// Remove the crop view
+			if ( ! _.isUndefined( this.views.get( 'crop' ) ) ) {
+				// Remove the imgAreaSelect API
+				if ( ! _.isEmpty( this.iasapi ) ) {
+					this.iasapi.remove();
+					this.iasapi = {};
+				}
+				crop = this.views.get( 'crop' );
+				crop.get( 'view' ).remove();
+				this.views.remove( { id: 'crop', view: crop } );
+			}
+
+			// Set the avatar !
+			bp.ajax.post( 'bp_avatar_set', {
+				json:          true,
+				original_file: avatar.get( 'url' ),
+				crop_w:        avatar.get( 'w' ),
+				crop_h:        avatar.get( 'h' ),
+				crop_x:        avatar.get( 'x' ),
+				crop_y:        avatar.get( 'y' ),
+				item_id:       avatar.get( 'item_id' ),
+				object:        avatar.get( 'object' ),
+				type:          _.isUndefined( avatar.get( 'type' ) ) ? 'crop' : avatar.get( 'type' ),
+				nonce:         avatar.get( 'nonces' ).set,
+			} ).done( function( response ) {
+				avatarStatus = new bp.Views.AvatarStatus( {
+					value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
+					type : 'success',
+				} );
+
+				self.views.add( {
+					id   : 'status',
+					view : avatarStatus
+				} );
+
+				avatarStatus.inject( '.bp-avatar-status' );
+
+				// Update each avatars of the page
+				$( '.' + avatar.get( 'object' ) + '-' + response.item_id + '-avatar' ).each( function() {
+					$(this).prop( 'src', response.avatar );
+				} );
+			} ).fail( function( response ) {
+				avatarStatus = new bp.Views.AvatarStatus( {
+					value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
+					type : 'error',
+				} );
+
+				self.views.add( {
+					id   : 'status',
+					view : avatarStatus
+				} );
+
+				avatarStatus.inject( '.bp-avatar-status' );
+			} );
+		},
+
+		deleteView:function() {
+			// Create the delete model
+			delete_model = new Backbone.Model( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
+				'object',
+				'item_id',
+				'nonces'
+			) );
+
+			// Create the delete view
+			deleteView = new bp.Views.DeleteAvatar( { model: delete_model } );
+
+			// Add it to views
+			this.views.add( { id: 'delete', view: deleteView } );
+
+			// Display it
+			deleteView.inject( '.bp-avatar' );
+		},
+
+		deleteAvatar: function( model ) {
+			var self = this,
+				deleteView;
+
+			// Remove the delete view
+			if ( ! _.isUndefined( this.views.get( 'delete' ) ) ) {
+				deleteView = this.views.get( 'delete' );
+				deleteView.get( 'view' ).remove();
+				this.views.remove( { id: 'delete', view: deleteView } );
+			}
+
+			// Remove the avatar !
+			bp.ajax.post( 'bp_avatar_delete', {
+				json:          true,
+				item_id:       model.get( 'item_id' ),
+				object:        model.get( 'object' ),
+				nonce:         model.get( 'nonces' ).delete,
+			} ).done( function( response ) {
+				avatarStatus = new bp.Views.AvatarStatus( {
+					value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
+					type : 'success',
+				} );
+
+				self.views.add( {
+					id   : 'status',
+					view : avatarStatus
+				} );
+
+				avatarStatus.inject( '.bp-avatar-status' );
+
+				// Update each avatars of the page
+				$( '.' + model.get( 'object' ) + '-' + response.item_id + '-avatar').each( function() {
+					$(this).prop( 'src', response.avatar );
+				} );
+
+				/**
+				 * Remove the delete tab
+				 * @todo this should be more dynamic using model.set()
+				 * - create the delete tab when new avatar is set
+				 * - remove the delete tab when avatar is deleted.
+				 *
+				 * For now, let's just remove the nav
+				 */
+				 $( '#bp-avatar-delete' ).remove();
+
+			} ).fail( function( response ) {
+				avatarStatus = new bp.Views.AvatarStatus( {
+					value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
+					type : 'error',
+				} );
+
+				self.views.add( {
+					id   : 'status',
+					view : avatarStatus
+				} );
+
+				avatarStatus.inject( '.bp-avatar-status' );
+			} );
+		}
+	};
+
+	// Main Nav view
+	bp.Views.Nav = bp.View.extend( {
+		tagName:    'ul',
+		className:  'avatar-nav-items',
+
+		events: {
+			'click .bp-avatar-nav-item' : 'toggleView',
+		},
+
+		initialize: function() {
+			_.each( this.collection.models, this.addNavItem, this );
+		},
+
+		addNavItem: function( item ) {
+			this.views.add( new bp.Views.NavItem( { model: item } ) );
+		},
+
+		toggleView: function( event ) {
+			event.preventDefault();
+
+			var active = $( event.target ).data( 'nav' );
+
+			_.each( this.collection.models, function( model ) {
+				if ( model.id == active ) {
+					model.set( { active: 1 } );
+					this.trigger( 'bp-avatar-view:changed', model.id )
+				} else {
+					model.set( { active: 0 } );
+				}
+			}, this );
+		},
+	} );
+
+	// Nav item view
+	bp.Views.NavItem = bp.View.extend( {
+		tagName:    'li',
+		className:  'avatar-nav-item',
+		template: bp.template( 'bp-avatar-nav' ),
+
+		initialize: function() {
+			if ( 1 == this.model.get( 'active' ) ) {
+				this.el.className += ' current';
+			}
+			this.el.id += 'bp-avatar-' + this.model.get( 'id' );
+
+			this.model.on( 'change:active', this.setCurrentNav, this );
+		},
+
+		setCurrentNav: function( model ) {
+			if ( 1 == model.get( 'active' ) ) {
+				this.$el.addClass( 'current' );
+			} else {
+				this.$el.removeClass( 'current' );
+			}
+		},
+	} );
+
+	// Avatars view
+	bp.Views.Avatars = bp.View.extend( {
+		className: "items",
+
+		initialize: function() {
+			_.each( this.collection.models, this.addItemView, this );
+		},
+
+		addItemView: function( item ) {
+			item.set( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
+				'object',
+				'item_id',
+				'nonces'
+			) );
+			this.views.add( new bp.Views.Avatar( { model: item } ) );
+		}
+	} );
+
+	// Avatar view
+	bp.Views.Avatar = bp.View.extend( {
+		className: "item",
+		template: bp.template( 'bp-avatar-item' ),
+
+		events: {
+			'click .avatar-crop-submit': 'cropAvatar',
+		},
+
+		initialize: function() {
+			_.defaults( this.options, {
+				full_h:  BP_Uploader.settings.crop.full_h,
+				full_w:  BP_Uploader.settings.crop.full_w,
+				aspectRatio : '150:150'
+			} );
+
+			this.on( 'ready', this.initCropper );
+		},
+
+		initCropper: function() {
+			var self = this,
+				tocrop = this.$el.find( '#avatar-to-crop img' ),
+				selection = {};
+
+			if ( ! _.isUndefined( this.options.full_h ) && ! _.isUndefined( this.options.full_w ) ) {
+				this.options.aspectRatio = this.options.full_h + ':' + this.options.full_w;
+			}
+
+			selection.w = this.model.get( 'width' );
+			selection.h = this.model.get( 'height' );
+
+			if ( selection.h <= selection.w ) {
+				crop_top    = Math.round( selection.h / 4 );
+				nh = nw     = Math.round( selection.h / 2 );
+				crop_bottom = nh + crop_top;
+				crop_left   = ( selection.w - nw ) / 2;
+				crop_right  = nw + crop_left;
+			} else {
+				crop_left   = Math.round( selection.w / 4 );
+				nh = nw     = Math.round( selection.w / 2 );
+				crop_right  = nw + crop_left;
+				crop_top    = ( selection.h - nh ) / 2;;
+				crop_bottom = nh + crop_top;
+			}
+
+			/**
+			 * Add the cropping interface
+			 * We're not specifying the parent option of the imgAreaSelect API
+			 * as it can be problematic with some themes (eg: twentyfifteen)
+			 */
+			bp.Avatar.iasapi = tocrop.imgAreaSelect( {
+				instance: true,
+				aspectRatio: self.options.aspectRatio,
+				handles:true,
+				x1: crop_left,
+				y1: crop_top,
+				x2: crop_right,
+				y2: crop_bottom,
+				onInit: function () {
+					self.model.set( { x: crop_left, y: crop_top, w: nw, h: nh } );
+					self.showPreview( self.model, self.options );
+				},
+				onSelectChange: function( img, c ) {
+					self.model.set( { x: c.x1, y: c.y1, w: c.width, h: c.height } );
+					self.showPreview( self.model, self.options );
+				}
+			} );
+		},
+
+		cropAvatar: function( event ) {
+			event.preventDefault();
+
+			bp.Avatar.setAvatar( this.model );
+		},
+
+		showPreview: function( model, options ) {
+			if ( ! model.get( 'w' ) || ! model.get( 'h' ) ) {
+				return;
+			}
+
+			if ( parseInt( model.get( 'w' ) ) > 0 ) {
+				var fw = options.full_w;
+				var fh = options.full_h;
+				var rx = fw / model.get( 'w' );
+				var ry = fh / model.get( 'h' );
+
+				$( '#avatar-crop-preview' ).css( {
+					maxWidth:'none',
+					width: Math.round( rx *  model.get( 'width' ) )+ 'px',
+					height: Math.round( ry * model.get( 'height' ) )+ 'px',
+					marginLeft: '-' + Math.round( rx * model.get( 'x' ) ) + 'px',
+					marginTop: '-' + Math.round( ry * model.get( 'y' ) ) + 'px'
+				} );
+			}
+		},
+
+	} );
+
+	// BuddyPress Avatar Feedback view
+	bp.Views.AvatarStatus = bp.View.extend( {
+		tagName: 'p',
+		className: 'updated',
+		id: 'bp-avatar-feedback',
+
+		initialize: function() {
+			this.el.className += ' ' + this.options.type;
+			this.value = this.options.value;
+		},
+
+		render: function() {
+			this.$el.html( this.value );
+			return this;
+		}
+	} );
+
+	// BuddyPress Avatar Delete view
+	bp.Views.DeleteAvatar = bp.View.extend( {
+		tagName: 'div',
+		id: 'bp-delete-avatar',
+		template: bp.template( 'bp-avatar-delete' ),
+
+		events: {
+			'click #bp-delete-avatar': 'deleteAvatar',
+		},
+
+		deleteAvatar: function( event ) {
+			event.preventDefault();
+
+			bp.Avatar.deleteAvatar( this.model );
+		}
+	} );
+
+	bp.Avatar.start();
+
+})( bp, jQuery );
diff --git src/bp-core/js/bp-plupload.js src/bp-core/js/bp-plupload.js
index e69de29..07f0959 100644
--- src/bp-core/js/bp-plupload.js
+++ src/bp-core/js/bp-plupload.js
@@ -0,0 +1,364 @@
+window.wp = window.wp || {};
+window.bp = window.bp || window.wp;
+
+( function( exports, $ ) {
+
+	// Bail if not set
+	if ( typeof BP_Uploader === 'undefined' ) {
+		return;
+	}
+
+	bp.Models      = bp.Models || {};
+	bp.Collections = bp.Collections || {};
+	bp.Views       = bp.Views || {};
+	bp.Uploader    = {};
+
+	/**
+	 * BuddyPress Uploader.
+	 *
+	 * This is an adapted version of wp.Uploader
+	 */
+	bp.Uploader.uploader = function() {
+		var self = this;
+		this.params  = BP_Uploader.settings;
+		this.strings = BP_Uploader.strings;
+
+		this.uploader    = new plupload.Uploader( this.params.defaults );
+		multipart_origin = this.uploader.settings.multipart_params;
+
+		/**
+		 * After the Uploader has been initialized, initialize some behaviors for the dropzone.
+		 *
+		 * @event Init
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 */
+		this.uploader.bind( 'Init', function( uplaoder ) {
+			var container = $( '#' + self.params.defaults.container ),
+			    drop_element = $( '#' + self.params.defaults.drop_element );
+
+			if ( uplaoder.features.dragdrop && ! $( document.body ).hasClass( 'mobile' ) ) {
+				container.addClass( 'drag-drop' );
+				drop_element.bind( 'dragover.wp-uploader', function() {
+					container.addClass( 'drag-over' );
+				} ).bind( 'dragleave.wp-uploader, drop.wp-uploader', function() {
+					container.removeClass( 'drag-over' );
+				} );
+			} else {
+				container.removeClass( 'drag-drop' );
+				drop_element.unbind( '.wp-uploader' );
+			}
+
+			if ( uplaoder.runtime == 'html4' ) {
+				$('.upload-flash-bypass').hide();
+			}
+
+		} );
+
+		// Init BuddyPress Uploader
+		this.uploader.init();
+
+		/**
+		 * Feedback callback.
+		 *
+		 * Add a new message to the errors collection, so it's possible
+		 * to give some feedback to the user
+		 *
+		 * @param  {string}        message
+		 * @param  {object}        data
+		 * @param  {plupload.File} file     File that was uploaded.
+		 */
+		feedback = function( message, data, file ) {
+			if ( ! _.isNull( file ) && file.item ) {
+				file.item.clear();
+			}
+
+			bp.Uploader.filesError.unshift( {
+				message: message,
+				data:    data,
+				file:    file
+			} );
+		};
+
+		/**
+		 * After files were filtered and added to the queue, create a model for each.
+		 *
+		 * @event FilesAdded
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {Array}             files    Array of file objects that were added to queue by the user.
+		 */
+		this.uploader.bind( 'FilesAdded', function( uploader, files ) {
+			hundredmb = 100 * 1024 * 1024, max = parseInt( uploader.settings.max_file_size, 10 );
+
+			/**
+			 * In case the multiple selection is false (eg: avatar) stop the process and send
+			 * and event containing a warning
+			 */
+			if ( ! uploader.settings.multi_selection && files.length > 1 ) {
+				for ( i in files ) {
+					uploader.removeFile( files[i] );
+				}
+
+				$(self).trigger( 'bp-uploader-warning', self.strings.unique_file_warning );
+				return;
+			}
+
+			_.each( files, function( file ) {
+				var attributes;
+
+				// Ignore failed uploads.
+				if ( plupload.FAILED === file.status ) {
+					return;
+				}
+
+				if ( max > hundredmb && file.size > hundredmb && up.runtime != 'html5' ) {
+					_this.uploadSizeError( uploader, file, true );
+				} else {
+					attributes = _.extend( {
+						id:        file.id,
+						file:      file,
+						uploading: true,
+						date:      new Date(),
+						filename:  file.name,
+					}, _.pick( file, 'loaded', 'size', 'percent' ) );
+
+					file.item = new bp.Models.File( attributes );
+					bp.Uploader.filesQueue.add( file.item );
+				}
+
+			} );
+
+			uploader.refresh();
+			uploader.start();
+		} );
+
+		/**
+		 * Update each file item on progress
+		 *
+		 * @event UploadProgress
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {Object}            file
+		 */
+		this.uploader.bind( 'UploadProgress', function( uploader, file ) {
+			file.item.set( _.pick( file, 'loaded', 'percent' ) );
+		} );
+
+		/**
+		 * After a file is successfully uploaded, update its model.
+		 *
+		 * @event FileUploaded
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {plupload.File}     file     File that was uploaded.
+		 * @param {Object}            response Object with response properties.
+		 * @return {mixed}
+		 */
+		this.uploader.bind( 'FileUploaded', function( uploader, file, response ) {
+			var complete,
+				message = self.strings.default_error;
+
+			try {
+				response = JSON.parse( response.response );
+			} catch ( e ) {
+				return feedback( message, e, file );
+			}
+
+			if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) {
+				return feedback( message, null, file );
+			} else if ( ! response.success ) {
+				if ( response.data && response.data.message ) {
+					message = response.data.message;
+				}
+
+				return feedback( message, response.data, file );
+			}
+
+			_.each(['file','loaded','size','percent'], function( key ) {
+				file.item.unset( key );
+			});
+
+			file.item.set( _.extend( response.data, { uploading: false } ) );
+
+			//  Add the file to the Uploaded ones
+			item = new bp.Models.File( response.data )
+			bp.Uploader.filesUploaded.add( item );
+		} );
+
+		/**
+		 * Trigger an event to inform a new upload is being processed
+		 *
+		 * Mainly used to remove an eventual warning
+		 *
+		 * @event BeforeUpload
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {Array}             files    Array of file objects that were added to queue by the user.
+		 */
+		this.uploader.bind( 'BeforeUpload', function( uploader, files ) {
+			$(self).trigger( 'bp-uploader-new-upload' );
+		} );
+
+		/**
+		 * Reset the filesQueue once the upload is complete
+		 *
+		 * @event BeforeUpload
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {Array}             files    Array of file objects that were added to queue by the user.
+		 */
+		this.uploader.bind( 'UploadComplete', function( uploader, files ) {
+			bp.Uploader.filesQueue.reset();
+		} );
+
+		/**
+		 * Map Plupload errors & Create a warning when plupload failed
+		 *
+		 * @event Error
+		 * @param {plupload.Uploader} uploader Uploader instance.
+		 * @param {Object}            pluploadError Plupload error
+		 */
+		this.uploader.bind( 'Error', function( uploader, pluploadError ) {
+			var message = self.strings.default_error,
+				key,
+				errors = {
+					'FAILED':                 self.strings.upload_failed,
+					'FILE_EXTENSION_ERROR':   self.strings.invalid_filetype,
+					'IMAGE_FORMAT_ERROR':     self.strings.not_an_image,
+					'IMAGE_MEMORY_ERROR':     self.strings.image_memory_exceeded,
+					'IMAGE_DIMENSIONS_ERROR': self.strings.image_dimensions_exceeded,
+					'GENERIC_ERROR':          self.strings.upload_failed,
+					'IO_ERROR':               self.strings.io_error,
+					'HTTP_ERROR':             self.strings.http_error,
+					'SECURITY_ERROR':         self.strings.security_error,
+					'FILE_SIZE_ERROR':        self.strings.file_exceeds_size_limit.replace( '%s' , pluploadError.file.name )
+				};
+
+			// Check for plupload errors.
+			for ( key in errors ) {
+				if ( pluploadError.code === plupload[ key ] ) {
+					message = errors[ key ];
+					break;
+				}
+			}
+
+			$(self).trigger( 'bp-uploader-warning', message );
+			uploader.refresh();
+		} );
+	}
+
+	// Create a very generic Model for files
+	bp.Models.File = Backbone.Model.extend( {
+		file: {},
+	} );
+
+	// Add Collections to store queue, uploaded files and errors
+	$.extend( bp.Uploader, {
+		filesQueue    : new Backbone.Collection(),
+		filesUploaded : new Backbone.Collection(),
+		filesError    : new Backbone.Collection()
+	} );
+
+	// Extend wp.Backbone.View with .prepare() and .inject()
+	bp.View = bp.Backbone.View.extend( {
+		inject: function( selector ) {
+			this.render();
+			$(selector).html( this.el );
+			this.views.ready();
+		},
+
+		prepare: function() {
+			if ( ! _.isUndefined( this.model ) && _.isFunction( this.model.toJSON ) ) {
+				return this.model.toJSON();
+			} else {
+				return {};
+			}
+		}
+	} );
+
+	// BuddyPress Uploader main view
+	bp.Views.Uploader = bp.View.extend( {
+		className: "bp-uploader-window",
+		template: bp.template( "upload-window" ),
+
+		defaults: _.pick( BP_Uploader.settings.defaults, 'container', 'drop_element', 'browse_button' ),
+
+		initialize: function() {
+			this.warning = null;
+			this.model = new Backbone.Model( this.defaults );
+			this.on( 'ready', this.initUploader );
+		},
+
+		initUploader: function() {
+			this.uploader = new bp.Uploader.uploader();
+			$( this.uploader ).on( 'bp-uploader-warning', _.bind( this.setWarning, this ) );
+			$( this.uploader ).on( 'bp-uploader-new-upload', _.bind( this.resetWarning, this ) );
+		},
+
+		setWarning: function( event, message ) {
+			if ( _.isUndefined( message ) ) {
+				return;
+			}
+
+			this.warning = new bp.Views.uploaderWarning( {
+				value: message
+			} ).render();
+
+			this.$el.after( this.warning.el );
+		},
+
+		resetWarning: function( event ) {
+			if ( _.isNull( this.warning ) ) {
+				return;
+			}
+
+			this.warning.remove();
+			this.warning = null;
+		}
+	} );
+
+	// BuddyPress Uploader warning view
+	bp.Views.uploaderWarning = bp.View.extend( {
+		tagName: 'p',
+		className: 'warning',
+		id: 'bp-uploader-warning',
+
+		initialize: function() {
+			this.value = this.options.value;
+		},
+
+		render: function() {
+			this.$el.html( this.value );
+			return this;
+		}
+	} );
+
+	// BuddyPress Uploader Files view
+	bp.Views.uploaderStatus = bp.View.extend( {
+		className: "files",
+
+		initialize: function() {
+			_.each( this.collection.models, this.addFile, this );
+			this.collection.on( 'change:percent', this.progress, this );
+			bp.Uploader.filesError.on( 'add', this.feedback, this );
+		},
+
+		addFile: function( file ) {
+			this.views.add( new bp.Views.uploaderProgress( { model: file } ) );
+		},
+
+		progress:function( model ) {
+			if ( ! _.isUndefined( model.get( 'percent' ) ) ) {
+				$( '#' + model.get('id') + ' .progress .bar' ).css( 'width', model.get('percent') + '%' );
+			}
+		},
+
+		feedback: function( model ) {
+			if ( ! _.isUndefined( model.get( 'message' ) ) && ! _.isUndefined( model.get( 'file' ) ) ) {
+				$( '#' + model.get( 'file' ).id ).html( model.get( 'message' ) ).addClass( 'error' );
+			}
+		}
+	} );
+
+	// BuddyPress Uploader File progress view
+	bp.Views.uploaderProgress = bp.View.extend( {
+		className: "bp-uploader-progress",
+		template: bp.template( "progress-window" ),
+	} );
+
+})( bp, jQuery );
diff --git src/bp-core/js/webcam.js src/bp-core/js/webcam.js
index e69de29..11b5fbc 100644
--- src/bp-core/js/webcam.js
+++ src/bp-core/js/webcam.js
@@ -0,0 +1,286 @@
+window.bp = window.bp || {};
+
+( function( exports, $ ) {
+
+	// Bail if not set
+	if ( typeof BP_Uploader === 'undefined' ) {
+		return;
+	}
+
+	bp.Models      = bp.Models || {};
+	bp.Collections = bp.Collections || {};
+	bp.Views       = bp.Views || {};
+
+	bp.WebCam = {
+		start: function() {
+			this.params = {
+				video:          null,
+				videoStream:    null,
+				capture_enable: false,
+				capture:        null,
+				canvas:         null,
+				warning:        null,
+			}
+
+			bp.Avatar.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
+		},
+
+		setView: function( view ) {
+			if ( 'camera' != view ) {
+				// Stop the camera if needed
+				if ( ! _.isNull( this.params.video ) ) {
+					this.stop();
+
+					// Remove all warnings as we're changing the view
+					this.removeWarning();
+				}
+
+				// Stop as this is not Camera area
+				return;
+			}
+
+			// Create the WebCam view
+			cameraView = new bp.Views.WebCamAvatar( { model: new Backbone.Model( { user_media: false } ) } );
+
+			// Add it to views
+			bp.Avatar.views.add( { id: 'camera', view: cameraView } );
+
+			// Display it
+	        cameraView.inject( '.bp-avatar' );
+		},
+
+		removeView: function() {
+			var camera;
+
+			if ( ! _.isUndefined( bp.Avatar.views.get( 'camera' ) ) ) {
+				camera = bp.Avatar.views.get( 'camera' );
+				camera.get( 'view' ).remove();
+				bp.Avatar.views.remove( { id: 'camera', view: camera } );
+			}
+		},
+
+		gotStream: function( stream ) {
+			var video = bp.WebCam.params.video;
+			bp.WebCam.params.videoStream = stream;
+
+			// User Feedback
+			bp.WebCam.displayWarning( 'loaded' );
+
+			video.onerror = function () {
+				// User Feedback
+				bp.WebCam.displayWarning( 'videoerror' );
+
+				if ( video ) {
+					bp.WebCam.stop();
+				}
+			};
+
+			stream.onended = bp.WebCam.noStream();
+
+			if ( video.mozSrcObject !== undefined ) {
+				video.mozSrcObject = stream;
+				video.play();
+			} else if ( navigator.mozGetUserMedia ) {
+				video.src = stream;
+				video.play();
+			} else if ( window.URL ) {
+				video.src = window.URL.createObjectURL( stream );
+			} else {
+				video.src = stream;
+			}
+
+			bp.WebCam.params.capture_enable = true;
+		},
+
+		stop: function() {
+			bp.WebCam.params.capture_enable = false;
+			if ( bp.WebCam.params.videoStream ) {
+				if ( bp.WebCam.params.videoStream.stop ) {
+					bp.WebCam.params.videoStream.stop();
+				} else if ( bp.WebCam.params.videoStream.msStop ) {
+					bp.WebCam.params.videoStream.msStop();
+				}
+				bp.WebCam.params.videoStream.onended = null;
+				bp.WebCam.params.videoStream = null;
+			}
+			if ( bp.WebCam.params.video ) {
+				bp.WebCam.params.video.onerror = null;
+				bp.WebCam.params.video.pause();
+				if ( bp.WebCam.params.video.mozSrcObject ) {
+					bp.WebCam.params.video.mozSrcObject = null;
+				}
+				bp.WebCam.params.video.src = "";
+			}
+		},
+
+		noStream: function() {
+			if ( _.isNull( bp.WebCam.params.videoStream ) ) {
+				// User Feedback
+				bp.WebCam.displayWarning( 'noaccess' );
+
+				bp.WebCam.removeView();
+			}
+		},
+
+		setAvatar: function( avatar ) {
+			if ( ! avatar.get( 'url' ) ) {
+				bp.WebCam.displayWarning( 'nocapture' );
+			}
+
+			// Remove the view
+			bp.WebCam.removeView();
+
+			bp.Avatar.setAvatar( avatar );
+		},
+
+		removeWarning: function() {
+			if ( ! _.isNull( this.params.warning ) ) {
+				this.params.warning.remove();
+			}
+		},
+
+		displayWarning: function( code ) {
+			this.removeWarning();
+
+			this.params.warning = new bp.Views.uploaderWarning( {
+				value: BP_Uploader.strings.camera_warnings[code]
+			} );
+
+			this.params.warning.inject( '.bp-avatar-status' );
+		},
+	};
+
+	// BuddyPress WebCam view
+	bp.Views.WebCamAvatar = bp.View.extend( {
+		tagName: 'div',
+		id: 'bp-webcam-avatar',
+		template: bp.template( 'bp-avatar-webcam' ),
+
+		events: {
+			'click .avatar-webcam-capture': 'captureStream',
+			'click .avatar-webcam-save': 'saveCapture',
+		},
+
+		initialize: function() {
+			var params;
+
+			if ( navigator.getUserMedia
+				|| navigator.oGetUserMedia
+				|| navigator.mozGetUserMedia
+				|| navigator.webkitGetUserMedia
+				|| navigator.msGetUserMedia ) {
+
+				// We need to add some cropping stuff to use bp.Avatar.setAvatar()
+				params = _.extend( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
+					'object',
+					'item_id',
+					'nonces'
+					), {
+						user_media:  true,
+						w: BP_Uploader.settings.crop.full_w,
+						h: BP_Uploader.settings.crop.full_h,
+						x: 0,
+						y: 0,
+						type: 'camera',
+					}
+				);
+
+				this.model.set( params );
+			}
+
+			this.on( 'ready', this.useStream, this );
+		},
+
+		useStream:function() {
+			// No support for user media... Stop!
+			if ( ! this.model.get( 'user_media' ) ) {
+				return;
+			}
+
+			this.options.video = new bp.Views.WebCamVideo();
+			this.options.canvas = new bp.Views.WebCamCanvas();
+
+			this.$el.find( '#avatar-to-crop' ).append( this.options.video.el );
+			this.$el.find( '#avatar-crop-pane' ).append( this.options.canvas.el );
+
+			bp.WebCam.params.video = this.options.video.el;
+			bp.WebCam.params.canvas = this.options.canvas.el;
+
+			// User Feedback
+			bp.WebCam.displayWarning( 'requesting' );
+
+			if ( navigator.getUserMedia ) {
+				navigator.getUserMedia( {video:true}, bp.WebCam.gotStream, bp.WebCams.noStream );
+			}  else if ( navigator.oGetUserMedia ) {
+				navigator.oGetUserMedia( {video:true}, bp.WebCam.gotStream, bp.WebCam.noStream );
+			} else if ( navigator.mozGetUserMedia ) {
+				navigator.mozGetUserMedia( {video:true}, bp.WebCam.gotStream, bp.WebCam.noStream );
+			} else if ( navigator.webkitGetUserMedia ) {
+				navigator.webkitGetUserMedia( {video:true}, bp.WebCam.gotStream, bp.WebCam.noStream );
+			} else if (navigator.msGetUserMedia) {
+				navigator.msGetUserMedia( {video:true, audio:false}, bp.WebCams.gotStream, bp.WebCam.noStream );
+			} else {
+				// User Feedback
+				bp.WebCam.displayWarning( 'errormsg' );
+			}
+		},
+
+		captureStream: function( event ) {
+			var sx, sc;
+			event.preventDefault();
+
+			if ( ! bp.WebCam.params.capture_enable ) {
+				// User Feedback
+				bp.WebCam.displayWarning( 'loading' );
+				return;
+			}
+
+			if ( 150 > this.options.video.el.videoHeight || 150 > this.options.video.el.videoWidth ) {
+				bp.WebCam.displayWarning( 'videoerror' );
+				return;
+			}
+
+			// Set the offset
+			sc = this.options.video.el.videoHeight;
+			sx = ( this.options.video.el.videoWidth - sc ) / 2;
+
+			this.options.canvas.el.getContext( '2d' ).drawImage( this.options.video.el, sx, 0, sc, sc, 0, 0, this.model.get( 'w' ), this.model.get( 'h' ) );
+			bp.WebCam.params.capture = this.options.canvas.el.toDataURL( "image/png" );
+			this.model.set( 'url', bp.WebCam.params.capture );
+
+			// User Feedback
+			bp.WebCam.displayWarning( 'ready' );
+		},
+
+		saveCapture: function( event ) {
+			event.preventDefault();
+
+			if ( ! bp.WebCam.params.capture ) {
+				// User Feedback
+				bp.WebCam.displayWarning( 'nocapture' );
+				return;
+			}
+
+			bp.WebCam.stop();
+			bp.WebCam.setAvatar( this.model );
+		},
+	} );
+
+	// BuddyPress Video stream view
+	bp.Views.WebCamVideo = bp.View.extend( {
+		tagName: 'video',
+		id: 'bp-webcam-video',
+		attributes: {
+			autoplay: 'autoplay',
+		}
+	} );
+
+	// BuddyPress Canvas (capture) view
+	bp.Views.WebCamCanvas = bp.View.extend( {
+		tagName: 'canvas',
+		id: 'bp-webcam-canvas',
+	} );
+
+	bp.WebCam.start();
+
+})( bp, jQuery );
diff --git src/bp-loader.php src/bp-loader.php
index 4ee261c..94df639 100644
--- src/bp-loader.php
+++ src/bp-loader.php
@@ -430,26 +430,27 @@ class BuddyPress {
 		require( $this->plugin_dir . 'bp-core/bp-core-theme-compatibility.php' );
 
 		// Require all of the BuddyPress core libraries
-		require( $this->plugin_dir . 'bp-core/bp-core-dependency.php' );
-		require( $this->plugin_dir . 'bp-core/bp-core-actions.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-caps.php'       );
-		require( $this->plugin_dir . 'bp-core/bp-core-cache.php'      );
-		require( $this->plugin_dir . 'bp-core/bp-core-cssjs.php'      );
-		require( $this->plugin_dir . 'bp-core/bp-core-update.php'     );
-		require( $this->plugin_dir . 'bp-core/bp-core-options.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-classes.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-taxonomy.php'   );
-		require( $this->plugin_dir . 'bp-core/bp-core-filters.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-avatars.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-widgets.php'    );
-		require( $this->plugin_dir . 'bp-core/bp-core-template.php'   );
-		require( $this->plugin_dir . 'bp-core/bp-core-adminbar.php'   );
-		require( $this->plugin_dir . 'bp-core/bp-core-buddybar.php'   );
-		require( $this->plugin_dir . 'bp-core/bp-core-catchuri.php'   );
-		require( $this->plugin_dir . 'bp-core/bp-core-component.php'  );
-		require( $this->plugin_dir . 'bp-core/bp-core-functions.php'  );
-		require( $this->plugin_dir . 'bp-core/bp-core-moderation.php' );
-		require( $this->plugin_dir . 'bp-core/bp-core-loader.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-dependency.php'  );
+		require( $this->plugin_dir . 'bp-core/bp-core-actions.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-caps.php'        );
+		require( $this->plugin_dir . 'bp-core/bp-core-cache.php'       );
+		require( $this->plugin_dir . 'bp-core/bp-core-cssjs.php'       );
+		require( $this->plugin_dir . 'bp-core/bp-core-update.php'      );
+		require( $this->plugin_dir . 'bp-core/bp-core-options.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-classes.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-taxonomy.php'    );
+		require( $this->plugin_dir . 'bp-core/bp-core-filters.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-attachments.php' );
+		require( $this->plugin_dir . 'bp-core/bp-core-avatars.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-widgets.php'     );
+		require( $this->plugin_dir . 'bp-core/bp-core-template.php'    );
+		require( $this->plugin_dir . 'bp-core/bp-core-adminbar.php'    );
+		require( $this->plugin_dir . 'bp-core/bp-core-buddybar.php'    );
+		require( $this->plugin_dir . 'bp-core/bp-core-catchuri.php'    );
+		require( $this->plugin_dir . 'bp-core/bp-core-component.php'   );
+		require( $this->plugin_dir . 'bp-core/bp-core-functions.php'   );
+		require( $this->plugin_dir . 'bp-core/bp-core-moderation.php'  );
+		require( $this->plugin_dir . 'bp-core/bp-core-loader.php'      );
 
 		// Skip or load deprecated content
 		if ( false !== $this->load_deprecated ) {
diff --git src/bp-templates/bp-attachments/avatars/camera.php src/bp-templates/bp-attachments/avatars/camera.php
index e69de29..79659c6 100644
--- src/bp-templates/bp-attachments/avatars/camera.php
+++ src/bp-templates/bp-attachments/avatars/camera.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * BuddyPress Avatars camera template
+ *
+ * This template is used to create the camera Backbone views
+ *
+ * @since 2.3
+ *
+ * @package BuddyPress
+ * @subpackage bp-attachments
+ */
+?>
+<script id="tmpl-bp-avatar-webcam" type="text/html">
+	<# if ( ! data.user_media ) { #>
+		<div id="bp-webcam-message">
+			<p class="warning"><?php esc_html_e( 'Your browser does not support the camera feature', 'buddypress' );?></p>
+		</div>
+	<# } else { #>
+		<div id="avatar-to-crop"></div>
+		<div id="avatar-crop-pane" class="avatar"></div>
+		<div id="avatar-crop-actions">
+			<a class="button avatar-webcam-capture" href="#"><?php esc_html_e( 'Capture', 'buddypress' );?></a>
+			<a class="button avatar-webcam-save hide" href="#"><?php esc_html_e( 'Save', 'buddypress' );?></a>
+		</div>
+	<# } #>
+</script>
diff --git src/bp-templates/bp-attachments/avatars/crop.php src/bp-templates/bp-attachments/avatars/crop.php
index e69de29..3f573c3 100644
--- src/bp-templates/bp-attachments/avatars/crop.php
+++ src/bp-templates/bp-attachments/avatars/crop.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * BuddyPress Avatars crop template
+ *
+ * This template is used to create the crop Backbone views
+ *
+ * @since 2.3
+ *
+ * @package BuddyPress
+ * @subpackage bp-attachments
+ */
+?>
+<script id="tmpl-bp-avatar-item" type="text/html">
+	<div id="avatar-to-crop">
+		<img src="{{data.url}}"/>
+	</div>
+	<div id="avatar-crop-pane" class="avatar">
+		<img src="{{data.url}}" id="avatar-crop-preview"/>
+	</div>
+	<div id="avatar-crop-actions">
+		<a class="button avatar-crop-submit" href="#"><?php esc_html_e( 'Crop Image', 'buddypress' ); ?></a>
+	</div>
+</script>
diff --git src/bp-templates/bp-attachments/avatars/index.php src/bp-templates/bp-attachments/avatars/index.php
index e69de29..bf63ae9 100644
--- src/bp-templates/bp-attachments/avatars/index.php
+++ src/bp-templates/bp-attachments/avatars/index.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * BuddyPress Avatars main template
+ *
+ * This template is used to inject the BuddyPress Backbone views
+ * dealing with avatars.
+ * It's also used to create the common Backbone views
+ *
+ * @since 2.3
+ *
+ * @package BuddyPress
+ * @subpackage bp-attachments
+ */
+
+
+/**
+ * This action is for internal use, please do not use it
+ */
+do_action( 'bp_attachments_avatar_check_template' );
+?>
+<div class="bp-avatar-nav"></div>
+<div class="bp-avatar"></div>
+<div class="bp-avatar-status"></div>
+
+<script type="text/html" id="tmpl-bp-avatar-nav">
+	<a href="{{data.href}}" class="bp-avatar-nav-item" data-nav="{{data.id}}">{{data.name}}</a>
+</script>
+
+<?php bp_get_template_part( 'uploader' ); ?>
+
+<?php bp_get_template_part( 'avatars/crop' ); ?>
+
+<?php bp_get_template_part( 'avatars/camera' ); ?>
+
+<script id="tmpl-bp-avatar-delete" type="text/html">
+	<# if ( 'user' == data.object ) { #>
+		<p><?php _e( "If you'd like to delete your current profile photo but not upload a new one, please use the delete profile photo button.", 'buddypress' ); ?></p>
+		<p><a class="button edit" id="bp-delete-avatar" href="#" title="<?php esc_attr_e( 'Delete Profile Photo', 'buddypress' ); ?>"><?php esc_html_e( 'Delete My Profile Photo', 'buddypress' ); ?></a></p>
+	<# } else if ( 'group' == data.object ) { #>
+		<p><?php _e( "If you'd like to remove the existing group profile photo but not upload a new one, please use the delete group profile photo button.", 'buddypress' ); ?></p>
+		<p><a class="button edit" id="bp-delete-avatar" href="#" title="<?php esc_attr_e( 'Delete Group Profile Photo', 'buddypress' ); ?>"><?php esc_html_e( 'Delete Group Profile Photo', 'buddypress' ); ?></a></p>
+	<# } else { #>
+		<?php do_action( 'bp_attachments_avatar_delete_template' ); ?>
+	<# } #>
+</script>
+
+<?php do_action( 'bp_attachments_avatar_main_template' ); ?>
diff --git src/bp-templates/bp-attachments/uploader.php src/bp-templates/bp-attachments/uploader.php
index e69de29..4c2b15b 100644
--- src/bp-templates/bp-attachments/uploader.php
+++ src/bp-templates/bp-attachments/uploader.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * BuddyPress Uploader templates
+ *
+ * This template is used to create the BuddyPress Uploader Backbone views
+ *
+ * @since 2.3
+ *
+ * @package BuddyPress
+ * @subpackage bp-attachments
+ */
+?>
+<script type="text/html" id="tmpl-upload-window">
+	<?php if ( ! _device_can_upload() ) : ?>
+		<h3 class="upload-instructions"><?php esc_html_e( 'The web browser on your device cannot be used to upload files.', 'buddypress' ); ?></h3>
+	<?php elseif ( is_multisite() && ! is_upload_space_available() ) : ?>
+		<h3 class="upload-instructions"><?php esc_html_e( 'Upload Limit Exceeded' ); ?></h3>
+	<?php else : ?>
+		<div id="{{data.container}}">
+			<div id="{{data.drop_element}}">
+				<div class="drag-drop-inside">
+					<p class="drag-drop-info"><?php esc_html_e( 'Drop your file here', 'buddypress' ); ?></p>
+					<p><?php _ex( 'or', 'Uploader: Drop your file here - or - Select your File', 'buddypress' ); ?></p>
+					<p class="drag-drop-buttons"><input id="{{data.browse_button}}" type="button" value="<?php esc_attr_e( 'Select your File', 'buddypress' ); ?>" class="button" /></p>
+				</div>
+			</div>
+		</div>
+	<?php endif; ?>
+</script>
+
+<script type="text/html" id="tmpl-progress-window">
+	<div id="{{data.id}}">
+		<div class="progress">
+			<div class="bar"></div>
+		</div>
+		<div class="filename">{{data.filename}}</div>
+	</div>
+</script>
diff --git src/bp-templates/bp-legacy/buddypress/groups/create.php src/bp-templates/bp-legacy/buddypress/groups/create.php
index 5741f10..f9cc672 100644
--- src/bp-templates/bp-legacy/buddypress/groups/create.php
+++ src/bp-templates/bp-legacy/buddypress/groups/create.php
@@ -60,7 +60,7 @@
 
 
 					<label>
-						<input type="radio" name="group-status" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> /> 
+						<input type="radio" name="group-status" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
 						<strong><?php _e( 'This is a private group', 'buddypress' ); ?></strong>
 					</label>
 					<ul>
@@ -71,7 +71,7 @@
 
 
 					<label>
-						<input type="radio" name="group-status" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> /> 
+						<input type="radio" name="group-status" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> />
 						<strong><?php _e('This is a hidden group', 'buddypress' ); ?></strong>
 					</label>
 					<ul>
@@ -153,6 +153,14 @@
 						<p><?php _e( 'To skip the group profile photo upload process, hit the "Next Step" button.', 'buddypress' ); ?></p>
 					</div><!-- .main-column -->
 
+					<?php
+					/**
+					 * Load the Avatar UI templates
+					 *
+					 * @since  BuddyPress (2.3.0)
+					 */
+					bp_core_avatar_get_template_part(); ?>
+
 				<?php endif; ?>
 
 				<?php if ( 'crop-image' == bp_get_avatar_admin_step() ) : ?>
@@ -306,4 +314,4 @@
 
 </div>
 
-<?php do_action( 'bp_after_create_group_page' ); ?>
\ No newline at end of file
+<?php do_action( 'bp_after_create_group_page' ); ?>
diff --git src/bp-templates/bp-legacy/buddypress/groups/single/admin.php src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
index a5811ff..1655959 100644
--- src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
+++ src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
@@ -140,6 +140,14 @@
 
 			<?php endif; ?>
 
+			<?php
+			/**
+			 * Load the Avatar UI templates
+			 *
+			 * @since  BuddyPress (2.3.0)
+			 */
+			bp_core_avatar_get_template_part(); ?>
+
 			<?php wp_nonce_field( 'bp_avatar_upload' ); ?>
 
 	<?php endif; ?>
diff --git src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php
index f2a3060..32cb0ba 100644
--- src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php
+++ src/bp-templates/bp-legacy/buddypress/members/single/profile/change-avatar.php
@@ -50,6 +50,14 @@
 
 	</form>
 
+	<?php
+	/**
+	 * Load the Avatar UI templates
+	 *
+	 * @since  BuddyPress (2.3.0)
+	 */
+	bp_core_avatar_get_template_part(); ?>
+
 <?php else : ?>
 
 	<p><?php _e( 'Your profile photo will be used on your profile and throughout the site. To change your profile photo, please create an account with <a href="http://gravatar.com">Gravatar</a> using the same email address as you used to register with this site.', 'buddypress' ); ?></p>
