diff --git a/examples/draw.rs b/examples/draw.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4dc0174e94d4894f60a45c39994e7ed6b7210bff
--- /dev/null
+++ b/examples/draw.rs
@@ -0,0 +1,210 @@
+// Copyright 2019 The xi-editor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! An example of an animating widget.
+
+use std::f64::consts::PI;
+
+use druid::kurbo::{BezPath, Line};
+use druid::widget::{Flex, Label, WidgetExt};
+use druid::{
+    AppLauncher, BoxConstraints, Color, Data, Env, Event, EventCtx, LayoutCtx, Lens,
+    LinearGradient, PaintCtx, PlatformError, Point, RenderContext, Size, UnitPoint, UpdateCtx,
+    Vec2, WheelEvent, Widget, WindowDesc,
+};
+
+use druid_widgets::Dial;
+use log::info;
+
+#[derive(Clone, Data, Lens, Default)]
+struct DialLabel {
+    value: f64,
+}
+
+impl DialLabel {
+    fn new(text: &str, value: f64) -> impl Widget<DialLabel> {
+        let text = text.to_string();
+        let label =
+            Label::new(move |data: &DialLabel, _env: &_| format!("{} {:.3}", text, data.value));
+        let solid = Color::rgb8(0x3a, 0x3a, 0x3a);
+        let mut col = Flex::column();
+        col.add_child(
+            Dial::new(value, Size::new(100.0, 100.0))
+                .lens(DialLabel::value)
+                .background(solid.clone())
+                .padding(5.0),
+            0.0,
+        );
+        col.add_child(label.padding(5.0).background(solid.clone()), 0.0);
+        col
+    }
+}
+
+#[derive(Clone, Data, Lens, Default)]
+struct Envelope {
+    attack: DialLabel,
+    decay: DialLabel,
+    sustain: DialLabel,
+    release: DialLabel,
+}
+
+impl Envelope {
+    fn new() -> impl Widget<Envelope> {
+        let solid = Color::rgb8(0x3a, 0x3a, 0x3a);
+        Flex::row()
+            .with_child(
+                DialLabel::new("Attack", 0.0)
+                    .lens(Envelope::attack)
+                    .background(solid.clone())
+                    .padding(5.0),
+                0.0,
+            )
+            .with_child(
+                DialLabel::new("Decay", 0.0)
+                    .lens(Envelope::decay)
+                    .background(solid.clone())
+                    .padding(5.0),
+                0.0,
+            )
+            .with_child(
+                DialLabel::new("Sustain", 0.0)
+                    .lens(Envelope::sustain)
+                    .background(solid.clone())
+                    .padding(5.0),
+                0.0,
+            )
+            .with_child(
+                DialLabel::new("Release", 0.0)
+                    .lens(Envelope::release)
+                    .background(solid.clone())
+                    .padding(5.0),
+                0.0,
+            )
+    }
+}
+
+// Draw has no internal state, just reflects the associated type
+#[derive(Clone, Data, Lens)]
+struct Draw {}
+
+impl Widget<Envelope> for Draw {
+    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut Envelope, _env: &Env) {}
+
+    fn update(
+        &mut self,
+        ctx: &mut UpdateCtx,
+        _old_data: Option<&Envelope>,
+        _data: &Envelope,
+        env: &Env,
+    ) {
+        //info!("update {:?}", env.get());
+        ctx.invalidate();
+    }
+
+    fn layout(
+        &mut self,
+        _layout_ctx: &mut LayoutCtx,
+        bc: &BoxConstraints,
+        _data: &Envelope,
+        _env: &Env,
+    ) -> Size {
+        bc.constrain((100.0, 100.0))
+    }
+
+    fn paint(&mut self, paint_ctx: &mut PaintCtx, data: &Envelope, _env: &Env) {
+        let size = paint_ctx.size();
+        info!("paint : {:?}", paint_ctx.size());
+        let mut path = BezPath::new();
+        let a = data.attack.value;
+        let d = data.decay.value;
+        let s = data.sustain.value;
+        let r = data.release.value;
+
+        let adr_time = a + d + r;
+        let sustain_time = adr_time / 3.0; // adjust the sustain time
+        let env_time = adr_time + sustain_time;
+        let x_scale = size.width / env_time;
+        let y_scale = size.height;
+
+        path.move_to(Point {
+            x: 0.,
+            y: size.height,
+        });
+        path.line_to(Point {
+            x: a * x_scale,
+            y: 0.,
+        });
+        path.line_to(Point {
+            x: (a + d) * x_scale,
+            y: size.height - s * y_scale,
+        });
+        path.line_to(Point {
+            x: (a + d + sustain_time) * x_scale,
+            y: size.height - s * y_scale,
+        });
+        path.line_to(Point {
+            x: size.width,
+            y: size.height,
+        });
+
+        paint_ctx.stroke(path, &Color::WHITE, 1.0);
+
+        // if let Some(p) = self.points.iter().next() {
+        //     let mut path = BezPath::new();
+        //     path.move_to(*p);
+
+        //     for p in self.points.iter() {
+        //         path.line_to(*p);
+        //     }
+
+        //     paint_ctx.stroke(path, &Color::WHITE, 1.0);
+        // }
+    }
+}
+
+#[derive(Clone, Data, Lens)]
+struct Adsr {
+    envelope: Envelope,
+}
+
+impl Adsr {
+    pub fn new() -> impl Widget<Adsr> {
+        let solid = Color::rgb8(0x3a, 0x3a, 0x3a);
+
+        Flex::column()
+            .with_child(
+                Draw {}
+                    .lens(Adsr::envelope)
+                    .background(solid.clone())
+                    .padding(5.0),
+                0.0,
+            )
+            .with_child(Envelope::new().lens(Adsr::envelope).padding(5.0), 0.0)
+    }
+}
+
+fn main() -> Result<(), PlatformError> {
+    AppLauncher::with_window(WindowDesc::new(Adsr::new))
+        .use_simple_logger()
+        .launch(Adsr {
+            envelope: Envelope {
+                attack: DialLabel { value: 0.1 },
+                decay: DialLabel { value: 0.2 },
+                sustain: DialLabel { value: 0.3 },
+                release: DialLabel { value: 0.4 },
+            },
+        })?;
+
+    Ok(())
+}