diff --git a/athena/athena.gyp b/athena/athena.gyp
index 1cbeaad7f3bac..659844332d0ce 100644
--- a/athena/athena.gyp
+++ b/athena/athena.gyp
@@ -15,6 +15,7 @@
         '../ui/accessibility/accessibility.gyp:ax_gen',
         '../ui/app_list/app_list.gyp:app_list',
         '../ui/aura/aura.gyp:aura',
+        '../ui/events/events.gyp:events_base',
         '../ui/strings/ui_strings.gyp:ui_strings',
         '../ui/views/views.gyp:views',
       ],
diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc
index b1dabcf954e47..a294cdec6e66d 100644
--- a/athena/wm/window_overview_mode.cc
+++ b/athena/wm/window_overview_mode.cc
@@ -14,8 +14,13 @@
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_property.h"
 #include "ui/aura/window_targeter.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_animation_observer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event_handler.h"
+#include "ui/events/gestures/fling_curve.h"
+#include "ui/gfx/frame_time.h"
 #include "ui/gfx/transform.h"
 #include "ui/wm/core/shadow.h"
 
@@ -93,7 +98,8 @@ class StaticWindowTargeter : public aura::WindowTargeter {
 };
 
 class WindowOverviewModeImpl : public WindowOverviewMode,
-                               public ui::EventHandler {
+                               public ui::EventHandler,
+                               public ui::CompositorAnimationObserver {
  public:
   WindowOverviewModeImpl(aura::Window* container,
                          WindowOverviewModeDelegate* delegate)
@@ -113,7 +119,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
 
   virtual ~WindowOverviewModeImpl() {
     container_->set_target_handler(container_->delegate());
-
+    RemoveAnimationObserver();
     const aura::Window::Windows& windows = container_->children();
     for (aura::Window::Windows::const_iterator iter = windows.begin();
          iter != windows.end();
@@ -287,6 +293,24 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
     return container_->bounds().height() * kScrollableFraction;
   }
 
+  void CreateFlingerFor(const ui::GestureEvent& event) {
+    gfx::Vector2dF velocity(event.details().velocity_x(),
+                            event.details().velocity_y());
+    fling_.reset(new ui::FlingCurve(velocity, gfx::FrameTime::Now()));
+  }
+
+  void AddAnimationObserver() {
+    ui::Compositor* compositor = container_->GetHost()->compositor();
+    if (!compositor->HasAnimationObserver(this))
+      compositor->AddAnimationObserver(this);
+  }
+
+  void RemoveAnimationObserver() {
+    ui::Compositor* compositor = container_->GetHost()->compositor();
+    if (compositor->HasAnimationObserver(this))
+      compositor->RemoveAnimationObserver(this);
+  }
+
   // ui::EventHandler:
   virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE {
     if (mouse->type() == ui::ET_MOUSE_PRESSED) {
@@ -314,12 +338,36 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
       }
     } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
       DoScroll(gesture->details().scroll_y());
+      gesture->SetHandled();
+    } else if (gesture->type() == ui::ET_SCROLL_FLING_START) {
+      CreateFlingerFor(*gesture);
+      AddAnimationObserver();
+      gesture->SetHandled();
+    } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN && fling_) {
+      fling_.reset();
+      RemoveAnimationObserver();
+      gesture->SetHandled();
+    }
+  }
+
+  // ui::CompositorAnimationObserver:
+  virtual void OnAnimationStep(base::TimeTicks timestamp) OVERRIDE {
+    CHECK(fling_);
+    if (fling_->start_timestamp() > timestamp)
+      return;
+    gfx::Vector2dF scroll = fling_->GetScrollAmountAtTime(timestamp);
+    if (scroll.IsZero()) {
+      fling_.reset();
+      RemoveAnimationObserver();
+    } else {
+      DoScroll(scroll.y());
     }
   }
 
   aura::Window* container_;
   WindowOverviewModeDelegate* delegate_;
   scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_;
+  scoped_ptr<ui::FlingCurve> fling_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl);
 };
diff --git a/ui/events/events.gyp b/ui/events/events.gyp
index ec7288a5021f9..b65b33bec9fb6 100644
--- a/ui/events/events.gyp
+++ b/ui/events/events.gyp
@@ -46,6 +46,8 @@
         'events_base_export.h',
         'gesture_event_details.cc',
         'gesture_event_details.h',
+        'gestures/fling_curve.cc',
+        'gestures/fling_curve.h',
         'gestures/gesture_configuration.cc',
         'gestures/gesture_configuration.h',
         'keycodes/keyboard_code_conversion.cc',
@@ -316,6 +318,7 @@
         'event_processor_unittest.cc',
         'event_rewriter_unittest.cc',
         'event_unittest.cc',
+        'gestures/fling_curve_unittest.cc',
         'gestures/motion_event_aura_unittest.cc',
         'gestures/velocity_calculator_unittest.cc',
         'gesture_detection/bitset_32_unittest.cc',
diff --git a/ui/events/gestures/fling_curve.cc b/ui/events/gestures/fling_curve.cc
new file mode 100644
index 0000000000000..6f63019510ac7
--- /dev/null
+++ b/ui/events/gestures/fling_curve.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/gestures/fling_curve.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace {
+
+const float kDefaultAlpha = -5.70762e+03f;
+const float kDefaultBeta = 1.72e+02f;
+const float kDefaultGamma = 3.7e+00f;
+
+inline double GetPositionAtTime(double t) {
+  return kDefaultAlpha * exp(-kDefaultGamma * t) - kDefaultBeta * t -
+         kDefaultAlpha;
+}
+
+inline double GetVelocityAtTime(double t) {
+  return -kDefaultAlpha * kDefaultGamma * exp(-kDefaultGamma * t) -
+         kDefaultBeta;
+}
+
+inline double GetTimeAtVelocity(double v) {
+  return -log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) /
+         kDefaultGamma;
+}
+
+}  // namespace
+
+namespace ui {
+
+FlingCurve::FlingCurve(const gfx::Vector2dF& velocity,
+                       base::TimeTicks start_timestamp)
+    : curve_duration_(GetTimeAtVelocity(0)),
+      start_timestamp_(start_timestamp),
+      time_offset_(0),
+      position_offset_(0) {
+  float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y()));
+  if (max_start_velocity > GetVelocityAtTime(0))
+    max_start_velocity = GetVelocityAtTime(0);
+  CHECK_GT(max_start_velocity, 0);
+
+  displacement_ratio_ = gfx::Vector2dF(velocity.x() / max_start_velocity,
+                                       velocity.y() / max_start_velocity);
+  time_offset_ = GetTimeAtVelocity(max_start_velocity);
+  position_offset_ = GetPositionAtTime(time_offset_);
+  last_timestamp_ = start_timestamp_ + base::TimeDelta::FromSecondsD(
+                                           curve_duration_ - time_offset_);
+}
+
+FlingCurve::~FlingCurve() {
+}
+
+gfx::Vector2dF FlingCurve::GetScrollAmountAtTime(base::TimeTicks current) {
+  if (current < start_timestamp_)
+    return gfx::Vector2dF();
+
+  float displacement = 0;
+  if (current < last_timestamp_) {
+    float time = time_offset_ + (current - start_timestamp_).InSecondsF();
+    CHECK_LT(time, curve_duration_);
+    displacement = GetPositionAtTime(time) - position_offset_;
+  } else {
+    displacement = GetPositionAtTime(curve_duration_) - position_offset_;
+  }
+
+  gfx::Vector2dF scroll(displacement * displacement_ratio_.x(),
+                        displacement * displacement_ratio_.y());
+  gfx::Vector2dF scroll_increment(scroll.x() - cumulative_scroll_.x(),
+                                  scroll.y() - cumulative_scroll_.y());
+  cumulative_scroll_ = scroll;
+  return scroll_increment;
+}
+
+}  // namespace ui
diff --git a/ui/events/gestures/fling_curve.h b/ui/events/gestures/fling_curve.h
new file mode 100644
index 0000000000000..583e172c999bb
--- /dev/null
+++ b/ui/events/gestures/fling_curve.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_GESTURES_FLING_CURVE_H_
+#define UI_EVENTS_GESTURES_FLING_CURVE_H_
+
+#include "base/time/time.h"
+#include "ui/events/events_base_export.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace ui {
+
+// FlingCurve can be used to scroll a UI element suitable for touch screen-based
+// flings.
+class EVENTS_BASE_EXPORT FlingCurve {
+ public:
+  FlingCurve(const gfx::Vector2dF& velocity, base::TimeTicks start_timestamp);
+  ~FlingCurve();
+
+  gfx::Vector2dF GetScrollAmountAtTime(base::TimeTicks current_timestamp);
+  base::TimeTicks start_timestamp() const { return start_timestamp_; }
+
+ private:
+  const float curve_duration_;
+  const base::TimeTicks start_timestamp_;
+
+  gfx::Vector2dF displacement_ratio_;
+  gfx::Vector2dF cumulative_scroll_;
+  base::TimeTicks last_timestamp_;
+  float time_offset_;
+  float position_offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(FlingCurve);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_GESTURES_FLING_CURVE_H_
diff --git a/ui/events/gestures/fling_curve_unittest.cc b/ui/events/gestures/fling_curve_unittest.cc
new file mode 100644
index 0000000000000..653a5effff8c9
--- /dev/null
+++ b/ui/events/gestures/fling_curve_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/gestures/fling_curve.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/frame_time.h"
+
+namespace ui {
+
+TEST(FlingCurveTest, Basic) {
+  const gfx::Vector2dF velocity(0, 5000);
+  base::TimeTicks now = gfx::FrameTime::Now();
+  FlingCurve curve(velocity, now);
+
+  gfx::Vector2dF scroll =
+      curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(20));
+  EXPECT_EQ(0, scroll.x());
+  EXPECT_NEAR(scroll.y(), 96, 1);
+
+  scroll =
+      curve.GetScrollAmountAtTime(now + base::TimeDelta::FromMilliseconds(250));
+  EXPECT_EQ(0, scroll.x());
+  EXPECT_NEAR(scroll.y(), 705, 1);
+
+  scroll =
+      curve.GetScrollAmountAtTime(now + base::TimeDelta::FromSeconds(10));
+  EXPECT_EQ(0, scroll.x());
+  EXPECT_NEAR(scroll.y(), 392, 1);
+
+  EXPECT_TRUE(curve.GetScrollAmountAtTime(
+      now + base::TimeDelta::FromSeconds(20)).IsZero());
+}
+
+}  // namespace ui