diff --git a/examples/sequence.rs b/examples/sequence.rs
index 375b5d9d5c83d8a5b3d7846c5e46737e839094fb..08d7ad7ca727fa7d3cc45e1cf5a2cfa1a14311b1 100644
--- a/examples/sequence.rs
+++ b/examples/sequence.rs
@@ -120,6 +120,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
                 end: pair[1] - center,
             },
         )
+        .with_completed_event(true) // Get an event after each segment
     }));
 
     commands
@@ -144,7 +145,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
             start: Vec3::new(-200., 100., 0.),
             end: Vec3::new(200., 100., 0.),
         },
-    );
+    )
+    .with_completed_event(true); // Get an event once move completed
     let tween_rotate = Tween::new(
         EaseFunction::QuadraticInOut,
         TweeningType::Once,
@@ -193,6 +195,7 @@ fn update_text(
     )>,
     query_anim_red: Query<&Animator<Transform>, With<RedSprite>>,
     query_anim_blue: Query<&Animator<Transform>, With<BlueSprite>>,
+    mut query_event: EventReader<TweenCompleted>,
 ) {
     let anim_red = query_anim_red.single();
     let tween_red = anim_red.tweenable().unwrap();
@@ -213,4 +216,8 @@ fn update_text(
         let mut blue_text = q1.single_mut();
         blue_text.sections[1].value = format!("{:5.1}%", progress_blue * 100.).to_string();
     }
+
+    for ev in query_event.iter() {
+        println!("Event: TweenCompleted entity={:?}", ev.entity);
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 5c26b3005b3d9b6196bf7cc1ec79af3e91ab1fbe..e48e947e29158babb6d10120679eccd71e6d2e04 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -165,7 +165,7 @@ mod tweenable;
 
 pub use lens::Lens;
 pub use plugin::{asset_animator_system, component_animator_system, TweeningPlugin};
-pub use tweenable::{Delay, Sequence, Tracks, Tween, TweenState, Tweenable};
+pub use tweenable::{Delay, Sequence, Tracks, Tween, TweenCompleted, TweenState, Tweenable};
 
 /// Type of looping for a tween animation.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/src/plugin.rs b/src/plugin.rs
index 9cc510d11efc8f4c6e5ff4eec778b040bf42bb14..8c695bcf223a53cc5fff024a4442cce37a59d234 100644
--- a/src/plugin.rs
+++ b/src/plugin.rs
@@ -1,6 +1,6 @@
 use bevy::{asset::Asset, ecs::component::Component, prelude::*};
 
-use crate::{Animator, AnimatorState, AssetAnimator};
+use crate::{Animator, AnimatorState, AssetAnimator, TweenCompleted};
 
 /// Plugin to add systems related to tweening of common components and assets.
 ///
@@ -33,7 +33,8 @@ pub struct TweeningPlugin;
 
 impl Plugin for TweeningPlugin {
     fn build(&self, app: &mut App) {
-        app.add_system(component_animator_system::<Transform>)
+        app.add_event::<TweenCompleted>()
+            .add_system(component_animator_system::<Transform>)
             .add_system(component_animator_system::<Text>)
             .add_system(component_animator_system::<Style>)
             .add_system(component_animator_system::<Sprite>)
@@ -48,11 +49,12 @@ impl Plugin for TweeningPlugin {
 pub fn component_animator_system<T: Component>(
     time: Res<Time>,
     mut query: Query<(Entity, &mut T, &mut Animator<T>)>,
+    mut event_writer: EventWriter<TweenCompleted>,
 ) {
     for (entity, ref mut target, ref mut animator) in query.iter_mut() {
         if animator.state != AnimatorState::Paused {
             if let Some(tweenable) = animator.tweenable_mut() {
-                tweenable.tick(time.delta(), target, entity);
+                tweenable.tick(time.delta(), target, entity, &mut event_writer);
             }
         }
     }
@@ -65,12 +67,13 @@ pub fn asset_animator_system<T: Asset>(
     time: Res<Time>,
     mut assets: ResMut<Assets<T>>,
     mut query: Query<(Entity, &mut AssetAnimator<T>)>,
+    mut event_writer: EventWriter<TweenCompleted>,
 ) {
     for (entity, ref mut animator) in query.iter_mut() {
         if animator.state != AnimatorState::Paused {
             if let Some(target) = assets.get_mut(animator.handle()) {
                 if let Some(tweenable) = animator.tweenable_mut() {
-                    tweenable.tick(time.delta(), target, entity);
+                    tweenable.tick(time.delta(), target, entity, &mut event_writer);
                 }
             }
         }
diff --git a/src/tweenable.rs b/src/tweenable.rs
index a58dbb3c5b3a2d3239b1d3e04a4abbef6341edc2..45d71ef801659126a981f8269bda4cba8244ee7f 100644
--- a/src/tweenable.rs
+++ b/src/tweenable.rs
@@ -16,6 +16,13 @@ pub enum TweenState {
     Completed,
 }
 
+/// Event raised when a tween completed.
+#[derive(Copy, Clone)]
+pub struct TweenCompleted {
+    /// The [`Entity`] the tween which completed and its animator are attached to.
+    pub entity: Entity,
+}
+
 /// An animatable entity, either a single [`Tween`] or a collection of them.
 pub trait Tweenable<T>: Send + Sync {
     /// Get the total duration of the animation.
@@ -59,7 +66,13 @@ pub trait Tweenable<T>: Send + Sync {
     ///
     /// [`rewind()`]: Tweenable::rewind
     /// [`set_progress()`]: Tweenable::set_progress
-    fn tick(&mut self, delta: Duration, target: &mut T, entity: Entity) -> TweenState;
+    fn tick(
+        &mut self,
+        delta: Duration,
+        target: &mut T,
+        entity: Entity,
+        event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState;
 
     /// Get the number of times this tweenable completed.
     ///
@@ -85,8 +98,14 @@ impl<T> Tweenable<T> for Box<dyn Tweenable<T> + Send + Sync + 'static> {
     fn progress(&self) -> f32 {
         self.as_ref().progress()
     }
-    fn tick(&mut self, delta: Duration, target: &mut T, entity: Entity) -> TweenState {
-        self.as_mut().tick(delta, target, entity)
+    fn tick(
+        &mut self,
+        delta: Duration,
+        target: &mut T,
+        entity: Entity,
+        event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState {
+        self.as_mut().tick(delta, target, entity, event_writer)
     }
     fn times_completed(&self) -> u32 {
         self.as_ref().times_completed()
@@ -117,6 +136,7 @@ pub struct Tween<T> {
     times_completed: u32,
     lens: Box<dyn Lens<T> + Send + Sync + 'static>,
     on_completed: Option<Box<dyn Fn(Entity, &Tween<T>) + Send + Sync + 'static>>,
+    raise_event: bool,
 }
 
 impl<T: 'static> Tween<T> {
@@ -187,9 +207,21 @@ impl<T> Tween<T> {
             times_completed: 0,
             lens: Box::new(lens),
             on_completed: None,
+            raise_event: false,
         }
     }
 
+    /// Enable or disable raising a completed event.
+    ///
+    /// If enabled, the tween will raise a [`TweenCompleted`] event when the animation completed.
+    /// This is similar to the [`set_completed`] callback, but uses Bevy events instead.
+    ///
+    /// [`set_completed`]: Tween::set_completed
+    pub fn with_completed_event(mut self, enabled: bool) -> Self {
+        self.raise_event = enabled;
+        self
+    }
+
     /// The current animation direction.
     ///
     /// See [`TweeningDirection`] for details.
@@ -214,6 +246,16 @@ impl<T> Tween<T> {
     pub fn clear_completed(&mut self) {
         self.on_completed = None;
     }
+
+    /// Enable or disable raising a completed event.
+    ///
+    /// If enabled, the tween will raise a [`TweenCompleted`] event when the animation completed.
+    /// This is similar to the [`set_completed`] callback, but uses Bevy events instead.
+    ///
+    /// [`set_completed`]: Tween::set_completed
+    pub fn set_completed_event(&mut self, enabled: bool) {
+        self.raise_event = enabled;
+    }
 }
 
 impl<T> Tweenable<T> for Tween<T> {
@@ -240,7 +282,13 @@ impl<T> Tweenable<T> for Tween<T> {
         }
     }
 
-    fn tick(&mut self, delta: Duration, target: &mut T, entity: Entity) -> TweenState {
+    fn tick(
+        &mut self,
+        delta: Duration,
+        target: &mut T,
+        entity: Entity,
+        event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState {
         if !self.is_looping() && self.timer.finished() {
             return TweenState::Completed;
         }
@@ -268,6 +316,9 @@ impl<T> Tweenable<T> for Tween<T> {
             // Timer::times_finished() returns the number of finished times since last tick only
             self.times_completed += self.timer.times_finished();
 
+            if self.raise_event {
+                event_writer.send(TweenCompleted { entity });
+            }
             if let Some(cb) = &self.on_completed {
                 cb(entity, &self);
             }
@@ -391,12 +442,18 @@ impl<T> Tweenable<T> for Sequence<T> {
         self.time.as_secs_f32() / self.duration.as_secs_f32()
     }
 
-    fn tick(&mut self, delta: Duration, target: &mut T, entity: Entity) -> TweenState {
+    fn tick(
+        &mut self,
+        delta: Duration,
+        target: &mut T,
+        entity: Entity,
+        event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState {
         if self.index < self.tweens.len() {
             let mut state = TweenState::Active;
             self.time = (self.time + delta).min(self.duration);
             let tween = &mut self.tweens[self.index];
-            let tween_state = tween.tick(delta, target, entity);
+            let tween_state = tween.tick(delta, target, entity, event_writer);
             if tween_state == TweenState::Completed {
                 tween.rewind();
                 self.index += 1;
@@ -471,11 +528,17 @@ impl<T> Tweenable<T> for Tracks<T> {
         self.time.as_secs_f32() / self.duration.as_secs_f32()
     }
 
-    fn tick(&mut self, delta: Duration, target: &mut T, entity: Entity) -> TweenState {
+    fn tick(
+        &mut self,
+        delta: Duration,
+        target: &mut T,
+        entity: Entity,
+        event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState {
         self.time = (self.time + delta).min(self.duration);
         let mut any_active = false;
         for tweenable in &mut self.tracks {
-            let state = tweenable.tick(delta, target, entity);
+            let state = tweenable.tick(delta, target, entity, event_writer);
             any_active = any_active || (state == TweenState::Active);
         }
         if any_active {
@@ -542,7 +605,13 @@ impl<T> Tweenable<T> for Delay {
         self.timer.percent()
     }
 
-    fn tick(&mut self, delta: Duration, _: &mut T, _entity: Entity) -> TweenState {
+    fn tick(
+        &mut self,
+        delta: Duration,
+        _target: &mut T,
+        _entity: Entity,
+        _event_writer: &mut EventWriter<TweenCompleted>,
+    ) -> TweenState {
         self.timer.tick(delta);
         if self.timer.finished() {
             TweenState::Completed
@@ -568,6 +637,7 @@ impl<T> Tweenable<T> for Delay {
 mod tests {
     use super::*;
     use crate::lens::*;
+    use bevy::ecs::{event::Events, system::SystemState};
     use std::sync::{Arc, Mutex};
     use std::time::Duration;
 
@@ -616,6 +686,13 @@ mod tests {
             });
             assert_eq!(callback_monitor.lock().unwrap().invoke_count, 0);
 
+            // Dummy world and event writer
+            let mut world = World::new();
+            world.insert_resource(Events::<TweenCompleted>::default());
+            let mut system_state: SystemState<EventWriter<TweenCompleted>> =
+                SystemState::new(&mut world);
+            let mut event_writer = system_state.get_mut(&mut world);
+
             // Loop over 2.2 seconds, so greater than one ping-pong loop
             let mut transform = Transform::default();
             let tick_duration = Duration::from_secs_f32(0.2);
@@ -664,7 +741,12 @@ mod tests {
                 );
 
                 // Tick the tween
-                let actual_state = tween.tick(tick_duration, &mut transform, dummy_entity);
+                let actual_state = tween.tick(
+                    tick_duration,
+                    &mut transform,
+                    dummy_entity,
+                    &mut event_writer,
+                );
 
                 // Check actual values
                 assert_eq!(tween.direction(), direction);
@@ -689,7 +771,12 @@ mod tests {
             assert_eq!(tween.times_completed(), 0);
 
             // Dummy tick to update target
-            let actual_state = tween.tick(Duration::ZERO, &mut transform, Entity::from_raw(0));
+            let actual_state = tween.tick(
+                Duration::ZERO,
+                &mut transform,
+                Entity::from_raw(0),
+                &mut event_writer,
+            );
             assert_eq!(actual_state, TweenState::Active);
             assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
             assert!(transform.rotation.abs_diff_eq(Quat::IDENTITY, 1e-5));
@@ -719,11 +806,20 @@ mod tests {
         );
         let mut seq = tween1.then(tween2);
         let mut transform = Transform::default();
+
+        // Dummy world and event writer
+        let mut world = World::new();
+        world.insert_resource(Events::<TweenCompleted>::default());
+        let mut system_state: SystemState<EventWriter<TweenCompleted>> =
+            SystemState::new(&mut world);
+        let mut event_writer = system_state.get_mut(&mut world);
+
         for i in 1..=16 {
             let state = seq.tick(
                 Duration::from_secs_f32(0.2),
                 &mut transform,
                 Entity::from_raw(0),
+                &mut event_writer,
             );
             if i < 5 {
                 assert_eq!(state, TweenState::Active);
@@ -769,11 +865,20 @@ mod tests {
         );
         let mut tracks = Tracks::new([tween1, tween2]);
         let mut transform = Transform::default();
+
+        // Dummy world and event writer
+        let mut world = World::new();
+        world.insert_resource(Events::<TweenCompleted>::default());
+        let mut system_state: SystemState<EventWriter<TweenCompleted>> =
+            SystemState::new(&mut world);
+        let mut event_writer = system_state.get_mut(&mut world);
+
         for i in 1..=6 {
             let state = tracks.tick(
                 Duration::from_secs_f32(0.2),
                 &mut transform,
                 Entity::from_raw(0),
+                &mut event_writer,
             );
             if i < 5 {
                 assert_eq!(state, TweenState::Active);