Skip to main content

news_feed/
news_feed.rs

1// News Feed System Architecture Animation
2// Inspired by "System Design Interview: An Insider's Guide" (Second Edition) by Alex Xu.
3
4use motion_canvas_rs::prelude::*;
5use std::time::Duration;
6
7const BG: Color = Color::rgb8(0x0e, 0x0e, 0x12);
8const WHITE: Color = Color::rgb8(0xf0, 0xf0, 0xf0);
9const DIM: Color = Color::rgb8(0x55, 0x55, 0x66);
10const ACCENT: Color = Color::rgb8(0x68, 0xab, 0xdf);
11const YELLOW: Color = Color::rgb8(0xe6, 0xa7, 0x00);
12const GREEN: Color = Color::rgb8(0x25, 0xc2, 0x81);
13const RED: Color = Color::rgb8(0xe1, 0x32, 0x38);
14
15trait HasOpacity {
16    fn opacity_signal(&self) -> Signal<f32>;
17}
18impl HasOpacity for TextNode {
19    fn opacity_signal(&self) -> Signal<f32> {
20        self.opacity.clone()
21    }
22}
23impl HasOpacity for Circle {
24    fn opacity_signal(&self) -> Signal<f32> {
25        self.opacity.clone()
26    }
27}
28impl HasOpacity for Rect {
29    fn opacity_signal(&self) -> Signal<f32> {
30        self.opacity.clone()
31    }
32}
33impl HasOpacity for Line {
34    fn opacity_signal(&self) -> Signal<f32> {
35        self.opacity.clone()
36    }
37}
38
39fn show(n: &impl HasOpacity, d: Duration) -> Box<dyn Animation> {
40    n.opacity_signal()
41        .to(1.0, d)
42        .ease(easings::cubic_out)
43        .into()
44}
45
46fn hide(n: &impl HasOpacity, d: Duration) -> Box<dyn Animation> {
47    n.opacity_signal().to(0.0, d).ease(easings::cubic_in).into()
48}
49
50// Helper for boxes to ensure they start completely hidden and slightly scaled down
51fn create_box(label: &str, center: Vec2, size: Vec2) -> (Rect, TextNode) {
52    let box_center = Vec2::new(center.x, center.y);
53
54    let text_pos = Vec2::new(center.x, center.y);
55
56    let r = Rect::default()
57        .with_position(box_center)
58        .with_size(size)
59        .with_scale(0.95)
60        .with_opacity(0.0)
61        .with_radius(8.0)
62        .with_stroke(ACCENT, 2.0)
63        .with_fill(Color::rgb8(0x26, 0x26, 0x2a));
64
65    let t = TextNode::default()
66        .with_position(text_pos)
67        .with_text(label)
68        .with_font_size(18.0)
69        .with_font("JetBrains Mono")
70        .with_fill(WHITE)
71        .with_scale(0.95)
72        .with_opacity(0.0);
73
74    (r, t)
75}
76
77fn create_line(start: Vec2, end: Vec2) -> (Line, Vec2) {
78    (
79        Line::default()
80            .with_start(start)
81            .with_end(start) // will animate to 'end'
82            .with_stroke(DIM, 2.0)
83            .with_opacity(0.0),
84        end,
85    )
86}
87
88fn main() {
89    let mut project = Project::default()
90        .with_fps(60)
91        .with_background(BG)
92        .with_title("News Feed System")
93        .with_dimensions(1920, 1080)
94        .close_on_finish();
95
96    // -- Title --
97    let title = TextNode::default()
98        .with_position(Vec2::new(80.0, 50.0))
99        .with_anchor(Vec2::new(-1.0, -1.0))
100        .with_text("News Feed Architecture")
101        .with_font_size(52.0)
102        .with_font("JetBrains Mono")
103        .with_fill(WHITE)
104        .with_scale(0.95)
105        .with_opacity(0.0);
106
107    // -- Top Tier --
108    let (user_box, user_label) = create_box(
109        "User (Web/App)",
110        Vec2::new(960.0, 130.0),
111        Vec2::new(200.0, 60.0),
112    );
113
114    let (line_lb, lb_end) = create_line(Vec2::new(960.0, 130.0), Vec2::new(960.0, 260.0));
115    let (lb_box, lb_label) = create_box(
116        "Load Balancer",
117        Vec2::new(960.0, 260.0),
118        Vec2::new(160.0, 60.0),
119    );
120
121    let (line_ws, ws_end) = create_line(Vec2::new(960.0, 260.0), Vec2::new(960.0, 390.0));
122    let (ws_box, ws_label) = create_box(
123        "Web Servers",
124        Vec2::new(960.0, 390.0),
125        Vec2::new(300.0, 80.0),
126    );
127
128    // -- Services Tier --
129    let (line_post, post_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(600.0, 520.0));
130    let (post_box, post_label) = create_box(
131        "Post Service",
132        Vec2::new(600.0, 520.0),
133        Vec2::new(160.0, 60.0),
134    );
135
136    let (line_fanout, fanout_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(960.0, 520.0));
137    let (fanout_box, fanout_label) = create_box(
138        "Fanout Service",
139        Vec2::new(960.0, 520.0),
140        Vec2::new(180.0, 60.0),
141    );
142
143    let (line_notif, notif_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(1320.0, 520.0));
144    let (notif_box, notif_label) = create_box(
145        "Notification Service",
146        Vec2::new(1320.0, 520.0),
147        Vec2::new(240.0, 60.0),
148    );
149
150    // -- Post Caches & DB --
151    let (line_post_cache, post_cache_end) =
152        create_line(Vec2::new(600.0, 520.0), Vec2::new(450.0, 720.0));
153    let (post_cache_box, post_cache_label) = create_box(
154        "Post Cache",
155        Vec2::new(450.0, 720.0),
156        Vec2::new(160.0, 80.0),
157    );
158
159    let (line_post_db, post_db_end) = create_line(Vec2::new(600.0, 520.0), Vec2::new(750.0, 720.0));
160    let (post_db_box, post_db_label) =
161        create_box("Post DB", Vec2::new(750.0, 720.0), Vec2::new(140.0, 80.0));
162
163    // -- NF Cache --
164    let (line_nf_cache, nf_cache_end) =
165        create_line(Vec2::new(960.0, 520.0), Vec2::new(960.0, 720.0));
166    let (nf_cache_box, nf_cache_label) = create_box(
167        "News Feed Cache",
168        Vec2::new(960.0, 720.0),
169        Vec2::new(200.0, 80.0),
170    );
171
172    // -- Fanout DBs Flow --
173    let (line_graph_db, graph_db_end) =
174        create_line(Vec2::new(960.0, 520.0), Vec2::new(1280.0, 620.0));
175    let (graph_db_box, graph_db_label) =
176        create_box("Graph DB", Vec2::new(1280.0, 620.0), Vec2::new(140.0, 60.0));
177
178    let (line_user_cache, user_cache_end) =
179        create_line(Vec2::new(960.0, 520.0), Vec2::new(1200.0, 720.0));
180    let (user_cache_box, user_cache_label) = create_box(
181        "User Cache",
182        Vec2::new(1200.0, 720.0),
183        Vec2::new(160.0, 80.0),
184    );
185
186    let (line_user_db, user_db_end) =
187        create_line(Vec2::new(1200.0, 720.0), Vec2::new(1200.0, 860.0));
188    let (user_db_box, user_db_label) =
189        create_box("User DB", Vec2::new(1200.0, 860.0), Vec2::new(140.0, 80.0));
190
191    // -- Case Indicators --
192    let case1 = TextNode::default()
193        .with_position(Vec2::new(80.0, 110.0))
194        .with_text("Scenario 1: Cache Miss (Data Fallback)")
195        .with_anchor(Vec2::new(-1.0, -1.0))
196        .with_font_size(24.0)
197        .with_font("JetBrains Mono")
198        .with_fill(YELLOW)
199        .with_opacity(0.0);
200
201    let case2 = TextNode::default()
202        .with_position(Vec2::new(80.0, 110.0))
203        .with_text("Scenario 2: Cache Hit (Fast Path)")
204        .with_anchor(Vec2::new(-1.0, -1.0))
205        .with_font_size(24.0)
206        .with_font("JetBrains Mono")
207        .with_fill(GREEN)
208        .with_opacity(0.0);
209
210    let case3 = TextNode::default()
211        .with_position(Vec2::new(80.0, 110.0))
212        .with_text("Scenario 3: Rate Limited (Packet Dropped)")
213        .with_font_size(24.0)
214        .with_anchor(Vec2::new(-1.0, -1.0))
215        .with_font("JetBrains Mono")
216        .with_fill(RED)
217        .with_opacity(0.0);
218
219    // -- Flow Step Indicators --
220
221    let case4 = TextNode::default()
222        .with_position(Vec2::new(80.0, 110.0))
223        .with_text("Scenario 4: Full Publish (Parallel)")
224        .with_font_size(24.0)
225        .with_anchor(Vec2::new(-1.0, -1.0))
226        .with_font("JetBrains Mono")
227        .with_fill(GREEN)
228        .with_opacity(0.0);
229
230    let step1 = TextNode::default()
231        .with_position(Vec2::new(1120.0, 570.0))
232        .with_text("1: Get Friend IDs")
233        .with_font_size(16.0)
234        .with_font("JetBrains Mono")
235        .with_fill(YELLOW)
236        .with_opacity(0.0);
237
238    let step2 = TextNode::default()
239        .with_position(Vec2::new(1060.0, 600.0))
240        .with_text("2: Get Friend Data")
241        .with_font_size(16.0)
242        .with_font("JetBrains Mono")
243        .with_fill(YELLOW)
244        .with_opacity(0.0);
245
246    let step3 = TextNode::default()
247        .with_position(Vec2::new(960.0, 620.0))
248        .with_text("3: Update Feed")
249        .with_font_size(16.0)
250        .with_font("JetBrains Mono")
251        .with_fill(YELLOW)
252        .with_opacity(0.0);
253
254    // Initial packet using exact center point
255    let packet = Circle::default()
256        .with_position(Vec2::new(960.0, 130.0))
257        .with_radius(12.0)
258        .with_scale(0.95)
259        .with_opacity(0.0)
260        .with_fill(GREEN);
261
262    let packet_post = Circle::default()
263        .with_position(Vec2::new(960.0, 130.0))
264        .with_radius(12.0)
265        .with_scale(0.95)
266        .with_opacity(0.0)
267        .with_fill(ACCENT);
268
269    let packet_notif = Circle::default()
270        .with_position(Vec2::new(960.0, 130.0))
271        .with_radius(12.0)
272        .with_scale(0.95)
273        .with_opacity(0.0)
274        .with_fill(YELLOW);
275
276    let fast_in = easings::cubic_out;
277    let smooth = easings::cubic_in_out;
278    let appear_dur = Duration::from_millis(250);
279    let move_dur = Duration::from_millis(700);
280
281    // -- Sequence --
282    project.scene.video_timeline.add(chain![
283        wait(Duration::from_millis(500)),
284        all![
285            show(&title, appear_dur),
286            title
287                .scale
288                .to(Vec2::new(1.0, 1.0), appear_dur)
289                .ease(fast_in)
290        ],
291        // Topology Staggered
292        sequence![
293            Duration::from_millis(60),
294            all![
295                show(&user_box, appear_dur),
296                user_box
297                    .scale
298                    .to(Vec2::new(1.0, 1.0), appear_dur)
299                    .ease(fast_in),
300                show(&user_label, appear_dur),
301                user_label
302                    .scale
303                    .to(Vec2::new(1.0, 1.0), appear_dur)
304                    .ease(fast_in),
305            ],
306            all![
307                show(&line_lb, appear_dur),
308                line_lb.end.to(lb_end, appear_dur).ease(smooth),
309                show(&lb_box, appear_dur),
310                lb_box
311                    .scale
312                    .to(Vec2::new(1.0, 1.0), appear_dur)
313                    .ease(fast_in),
314                show(&lb_label, appear_dur),
315                lb_label
316                    .scale
317                    .to(Vec2::new(1.0, 1.0), appear_dur)
318                    .ease(fast_in),
319            ],
320            all![
321                show(&line_ws, appear_dur),
322                line_ws.end.to(ws_end, appear_dur).ease(smooth),
323                show(&ws_box, appear_dur),
324                ws_box
325                    .scale
326                    .to(Vec2::new(1.0, 1.0), appear_dur)
327                    .ease(fast_in),
328                show(&ws_label, appear_dur),
329                ws_label
330                    .scale
331                    .to(Vec2::new(1.0, 1.0), appear_dur)
332                    .ease(fast_in),
333            ]
334        ],
335        sequence![
336            Duration::from_millis(60),
337            all![
338                show(&line_post, appear_dur),
339                line_post.end.to(post_end, appear_dur).ease(smooth),
340                show(&post_box, appear_dur),
341                post_box
342                    .scale
343                    .to(Vec2::new(1.0, 1.0), appear_dur)
344                    .ease(fast_in),
345                show(&post_label, appear_dur),
346                post_label
347                    .scale
348                    .to(Vec2::new(1.0, 1.0), appear_dur)
349                    .ease(fast_in),
350            ],
351            all![
352                show(&line_fanout, appear_dur),
353                line_fanout.end.to(fanout_end, appear_dur).ease(smooth),
354                show(&fanout_box, appear_dur),
355                fanout_box
356                    .scale
357                    .to(Vec2::new(1.0, 1.0), appear_dur)
358                    .ease(fast_in),
359                show(&fanout_label, appear_dur),
360                fanout_label
361                    .scale
362                    .to(Vec2::new(1.0, 1.0), appear_dur)
363                    .ease(fast_in),
364            ],
365            all![
366                show(&line_notif, appear_dur),
367                line_notif.end.to(notif_end, appear_dur).ease(smooth),
368                show(&notif_box, appear_dur),
369                notif_box
370                    .scale
371                    .to(Vec2::new(1.0, 1.0), appear_dur)
372                    .ease(fast_in),
373                show(&notif_label, appear_dur),
374                notif_label
375                    .scale
376                    .to(Vec2::new(1.0, 1.0), appear_dur)
377                    .ease(fast_in),
378            ],
379        ],
380        sequence![
381            Duration::from_millis(60),
382            all![
383                show(&line_post_cache, appear_dur),
384                line_post_cache
385                    .end
386                    .to(post_cache_end, appear_dur)
387                    .ease(smooth),
388                show(&post_cache_box, appear_dur),
389                post_cache_box
390                    .scale
391                    .to(Vec2::new(1.0, 1.0), appear_dur)
392                    .ease(fast_in),
393                show(&post_cache_label, appear_dur),
394                post_cache_label
395                    .scale
396                    .to(Vec2::new(1.0, 1.0), appear_dur)
397                    .ease(fast_in),
398            ],
399            all![
400                show(&line_post_db, appear_dur),
401                line_post_db.end.to(post_db_end, appear_dur).ease(smooth),
402                show(&post_db_box, appear_dur),
403                post_db_box
404                    .scale
405                    .to(Vec2::new(1.0, 1.0), appear_dur)
406                    .ease(fast_in),
407                show(&post_db_label, appear_dur),
408                post_db_label
409                    .scale
410                    .to(Vec2::new(1.0, 1.0), appear_dur)
411                    .ease(fast_in),
412            ],
413            all![
414                show(&line_nf_cache, appear_dur),
415                line_nf_cache.end.to(nf_cache_end, appear_dur).ease(smooth),
416                show(&nf_cache_box, appear_dur),
417                nf_cache_box
418                    .scale
419                    .to(Vec2::new(1.0, 1.0), appear_dur)
420                    .ease(fast_in),
421                show(&nf_cache_label, appear_dur),
422                nf_cache_label
423                    .scale
424                    .to(Vec2::new(1.0, 1.0), appear_dur)
425                    .ease(fast_in),
426            ],
427            all![
428                show(&line_graph_db, appear_dur),
429                line_graph_db.end.to(graph_db_end, appear_dur).ease(smooth),
430                show(&graph_db_box, appear_dur),
431                graph_db_box
432                    .scale
433                    .to(Vec2::new(1.0, 1.0), appear_dur)
434                    .ease(fast_in),
435                show(&graph_db_label, appear_dur),
436                graph_db_label
437                    .scale
438                    .to(Vec2::new(1.0, 1.0), appear_dur)
439                    .ease(fast_in),
440            ],
441            all![
442                show(&line_user_cache, appear_dur),
443                line_user_cache
444                    .end
445                    .to(user_cache_end, appear_dur)
446                    .ease(smooth),
447                show(&user_cache_box, appear_dur),
448                user_cache_box
449                    .scale
450                    .to(Vec2::new(1.0, 1.0), appear_dur)
451                    .ease(fast_in),
452                show(&user_cache_label, appear_dur),
453                user_cache_label
454                    .scale
455                    .to(Vec2::new(1.0, 1.0), appear_dur)
456                    .ease(fast_in),
457            ],
458            all![
459                show(&line_user_db, appear_dur),
460                line_user_db.end.to(user_db_end, appear_dur).ease(smooth),
461                show(&user_db_box, appear_dur),
462                user_db_box
463                    .scale
464                    .to(Vec2::new(1.0, 1.0), appear_dur)
465                    .ease(fast_in),
466                show(&user_db_label, appear_dur),
467                user_db_label
468                    .scale
469                    .to(Vec2::new(1.0, 1.0), appear_dur)
470                    .ease(fast_in),
471            ]
472        ],
473        wait(Duration::from_secs(1)),
474        // ==========================================
475        //  SCENARIO 1: CACHE MISS
476        // ==========================================
477        all![
478            show(&case1, appear_dur),
479            show(&packet, Duration::from_millis(200)),
480            packet.fill_color.to(GREEN, Duration::from_millis(0)), // Start healthy
481            packet
482                .scale
483                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
484                .ease(fast_in),
485            packet
486                .position
487                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
488        ],
489        packet
490            .position
491            .to(Vec2::new(960.0, 260.0), move_dur)
492            .ease(smooth),
493        packet
494            .position
495            .to(Vec2::new(960.0, 390.0), move_dur)
496            .ease(smooth),
497        packet
498            .position
499            .to(Vec2::new(960.0, 520.0), move_dur)
500            .ease(smooth),
501        wait(Duration::from_millis(200)),
502        // 1: Graph DB Check
503        all![
504            packet
505                .position
506                .to(Vec2::new(1280.0, 620.0), move_dur)
507                .ease(smooth),
508            delay![move_dur / 2, show(&step1, appear_dur)]
509        ],
510        packet
511            .position
512            .to(Vec2::new(960.0, 520.0), move_dur)
513            .ease(smooth),
514        // 2: User Cache MISSED -> Query DB
515        all![
516            packet
517                .position
518                .to(Vec2::new(1200.0, 720.0), move_dur)
519                .ease(smooth),
520            delay![move_dur / 2, show(&step2, appear_dur)]
521        ],
522        // Alert Miss: Flash Yellow and head to DB
523        all![
524            packet.fill_color.to(YELLOW, Duration::from_millis(200)),
525            packet
526                .scale
527                .to(Vec2::new(1.2, 1.2), Duration::from_millis(200))
528                .ease(easings::bounce_out),
529            delay![
530                Duration::from_millis(200),
531                packet
532                    .position
533                    .to(Vec2::new(1200.0, 860.0), move_dur)
534                    .ease(smooth)
535            ]
536        ],
537        // Return from DB to Fanout smoothly
538        all![
539            packet.fill_color.to(GREEN, Duration::from_millis(200)),
540            packet
541                .scale
542                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200)),
543            packet
544                .position
545                .to(Vec2::new(1200.0, 720.0), move_dur)
546                .ease(smooth)
547        ],
548        packet
549            .position
550            .to(Vec2::new(960.0, 520.0), move_dur)
551            .ease(smooth),
552        // 3: Write to News Feed Cache
553        all![
554            packet
555                .position
556                .to(Vec2::new(960.0, 720.0), move_dur)
557                .ease(smooth),
558            delay![move_dur / 2, show(&step3, appear_dur)]
559        ],
560        packet
561            .position
562            .to(Vec2::new(960.0, 520.0), move_dur)
563            .ease(smooth),
564        wait(Duration::from_millis(200)),
565        // Packet Flow (Back Track to User)
566        packet
567            .position
568            .to(Vec2::new(960.0, 390.0), move_dur)
569            .ease(smooth),
570        packet
571            .position
572            .to(Vec2::new(960.0, 260.0), move_dur)
573            .ease(smooth),
574        packet
575            .position
576            .to(Vec2::new(960.0, 130.0), move_dur)
577            .ease(smooth),
578        // Hide packet & Case states
579        all![
580            hide(&packet, Duration::from_millis(200)),
581            hide(&case1, appear_dur),
582            hide(&step1, appear_dur),
583            hide(&step2, appear_dur),
584            hide(&step3, appear_dur),
585        ],
586        wait(Duration::from_secs(1)),
587        // ==========================================
588        //  SCENARIO 2: CACHE HIT
589        // ==========================================
590        all![
591            show(&case2, appear_dur),
592            show(&packet, Duration::from_millis(200)),
593            packet
594                .position
595                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
596            packet
597                .scale
598                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
599                .ease(fast_in),
600        ],
601        packet
602            .position
603            .to(Vec2::new(960.0, 260.0), move_dur)
604            .ease(smooth),
605        packet
606            .position
607            .to(Vec2::new(960.0, 390.0), move_dur)
608            .ease(smooth),
609        packet
610            .position
611            .to(Vec2::new(960.0, 520.0), move_dur)
612            .ease(smooth),
613        // 1: Graph DB
614        all![
615            packet
616                .position
617                .to(Vec2::new(1280.0, 620.0), move_dur)
618                .ease(smooth),
619            delay![move_dur / 2, show(&step1, appear_dur)]
620        ],
621        packet
622            .position
623            .to(Vec2::new(960.0, 520.0), move_dur)
624            .ease(smooth),
625        // 2: User Cache HIT (Skip DB entirely)
626        all![
627            packet
628                .position
629                .to(Vec2::new(1200.0, 720.0), move_dur)
630                .ease(smooth),
631            delay![move_dur / 2, show(&step2, appear_dur)]
632        ],
633        packet
634            .position
635            .to(Vec2::new(960.0, 520.0), move_dur)
636            .ease(smooth),
637        // 3: News Feed Cache
638        all![
639            packet
640                .position
641                .to(Vec2::new(960.0, 720.0), move_dur)
642                .ease(smooth),
643            delay![move_dur / 2, show(&step3, appear_dur)]
644        ],
645        packet
646            .position
647            .to(Vec2::new(960.0, 520.0), move_dur)
648            .ease(smooth),
649        // Return
650        packet
651            .position
652            .to(Vec2::new(960.0, 390.0), move_dur)
653            .ease(smooth),
654        packet
655            .position
656            .to(Vec2::new(960.0, 260.0), move_dur)
657            .ease(smooth),
658        packet
659            .position
660            .to(Vec2::new(960.0, 130.0), move_dur)
661            .ease(smooth),
662        all![
663            hide(&packet, Duration::from_millis(200)),
664            hide(&case2, appear_dur),
665            hide(&step1, appear_dur),
666            hide(&step2, appear_dur),
667            hide(&step3, appear_dur),
668        ],
669        wait(Duration::from_secs(1)),
670        // ==========================================
671        //  SCENARIO 3: DROPPED PACKET
672        // ==========================================
673        all![
674            show(&case3, appear_dur),
675            show(&packet, Duration::from_millis(200)),
676            packet.fill_color.to(GREEN, Duration::from_millis(0)),
677            packet
678                .position
679                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
680            packet
681                .scale
682                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
683                .ease(fast_in),
684        ],
685        // To LB
686        packet
687            .position
688            .to(Vec2::new(960.0, 260.0), move_dur)
689            .ease(smooth),
690        // Moving to Web Servers but fails mid-way / at LB
691        all![
692            packet
693                .position
694                .to(Vec2::new(960.0, 390.0), move_dur)
695                .ease(smooth),
696            // Turns Red and gets destroyed
697            sequence![
698                move_dur / 2, // Wait until halfway
699                all![
700                    packet.fill_color.to(RED, Duration::from_millis(150)),
701                    packet
702                        .scale
703                        .to(Vec2::new(1.8, 1.8), Duration::from_millis(150))
704                        .ease(easings::elastic_out),
705                ],
706                all![
707                    hide(&packet, Duration::from_millis(200)),
708                    packet
709                        .scale
710                        .to(Vec2::new(0.0, 0.0), Duration::from_millis(200))
711                        .ease(easings::cubic_in),
712                ]
713            ]
714        ],
715        hide(&case3, appear_dur),
716        wait(Duration::from_secs(1)),
717        // ==========================================
718        //  SCENARIO 4: PARALLEL PUBLISH
719        // ==========================================
720        all![
721            show(&case4, appear_dur),
722            show(&packet, Duration::from_millis(200)),
723            show(&packet_post, Duration::from_millis(200)),
724            show(&packet_notif, Duration::from_millis(200)),
725            packet.fill_color.to(GREEN, Duration::from_millis(0)),
726            packet
727                .position
728                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
729            packet
730                .scale
731                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
732                .ease(fast_in),
733            packet_post
734                .position
735                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
736            packet_post
737                .scale
738                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
739                .ease(fast_in),
740            packet_notif
741                .position
742                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
743            packet_notif
744                .scale
745                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
746                .ease(fast_in),
747        ],
748        // Flow down collectively to Web Servers
749        all![
750            packet
751                .position
752                .to(Vec2::new(960.0, 260.0), move_dur)
753                .ease(smooth),
754            packet_post
755                .position
756                .to(Vec2::new(960.0, 260.0), move_dur)
757                .ease(smooth),
758            packet_notif
759                .position
760                .to(Vec2::new(960.0, 260.0), move_dur)
761                .ease(smooth),
762        ],
763        all![
764            packet
765                .position
766                .to(Vec2::new(960.0, 390.0), move_dur)
767                .ease(smooth),
768            packet_post
769                .position
770                .to(Vec2::new(960.0, 390.0), move_dur)
771                .ease(smooth),
772            packet_notif
773                .position
774                .to(Vec2::new(960.0, 390.0), move_dur)
775                .ease(smooth),
776        ],
777        // BRANCHING!
778        all![
779            // Main packet goes to fanout
780            packet
781                .position
782                .to(Vec2::new(960.0, 520.0), move_dur)
783                .ease(smooth),
784            // Post packet to Post Service
785            packet_post
786                .position
787                .to(Vec2::new(600.0, 520.0), move_dur)
788                .ease(smooth),
789            // Notif packet to Notification Service
790            packet_notif
791                .position
792                .to(Vec2::new(1320.0, 520.0), move_dur)
793                .ease(smooth),
794        ],
795        wait(Duration::from_millis(200)),
796        // Post DB, NF Cache, Notifications APNS fly off
797        all![
798            // Fanout updates NF Cache
799            packet
800                .position
801                .to(Vec2::new(960.0, 720.0), move_dur)
802                .ease(smooth),
803            // Post updates DB
804            packet_post
805                .position
806                .to(Vec2::new(750.0, 720.0), move_dur)
807                .ease(smooth),
808            // Notif flies off-screen (Apple Push Notification Server etc)
809            packet_notif
810                .position
811                .to(Vec2::new(1500.0, 520.0), move_dur)
812                .ease(smooth),
813            hide(&packet_notif, move_dur),
814        ],
815        wait(Duration::from_millis(200)),
816        // Return
817        all![
818            packet
819                .position
820                .to(Vec2::new(960.0, 520.0), move_dur)
821                .ease(smooth),
822            packet_post
823                .position
824                .to(Vec2::new(600.0, 520.0), move_dur)
825                .ease(smooth),
826        ],
827        all![
828            packet
829                .position
830                .to(Vec2::new(960.0, 390.0), move_dur)
831                .ease(smooth),
832            packet_post
833                .position
834                .to(Vec2::new(960.0, 390.0), move_dur)
835                .ease(smooth),
836        ],
837        all![
838            packet
839                .position
840                .to(Vec2::new(960.0, 260.0), move_dur)
841                .ease(smooth),
842            packet_post
843                .position
844                .to(Vec2::new(960.0, 260.0), move_dur)
845                .ease(smooth),
846        ],
847        all![
848            packet
849                .position
850                .to(Vec2::new(960.0, 130.0), move_dur)
851                .ease(smooth),
852            packet_post
853                .position
854                .to(Vec2::new(960.0, 130.0), move_dur)
855                .ease(smooth),
856        ],
857        // Hide
858        all![
859            hide(&packet, Duration::from_millis(200)),
860            hide(&packet_post, Duration::from_millis(200)),
861            hide(&case4, appear_dur),
862        ],
863        wait(Duration::from_secs(2)),
864    ]);
865
866    // -- Adding to Scene --
867    for element in [
868        Box::new(title) as Box<dyn Node>,
869        Box::new(line_lb),
870        Box::new(line_ws),
871        Box::new(line_post),
872        Box::new(line_fanout),
873        Box::new(line_notif),
874        Box::new(line_post_cache),
875        Box::new(line_post_db),
876        Box::new(line_nf_cache),
877        Box::new(line_graph_db),
878        Box::new(line_user_cache),
879        Box::new(line_user_db),
880        Box::new(user_box),
881        Box::new(user_label),
882        Box::new(lb_box),
883        Box::new(lb_label),
884        Box::new(ws_box),
885        Box::new(ws_label),
886        Box::new(post_box),
887        Box::new(post_label),
888        Box::new(fanout_box),
889        Box::new(fanout_label),
890        Box::new(notif_box),
891        Box::new(notif_label),
892        Box::new(post_cache_box),
893        Box::new(post_cache_label),
894        Box::new(post_db_box),
895        Box::new(post_db_label),
896        Box::new(nf_cache_box),
897        Box::new(nf_cache_label),
898        Box::new(graph_db_box),
899        Box::new(graph_db_label),
900        Box::new(user_cache_box),
901        Box::new(user_cache_label),
902        Box::new(user_db_box),
903        Box::new(user_db_label),
904        Box::new(case1),
905        Box::new(case2),
906        Box::new(case3),
907        Box::new(step1),
908        Box::new(step2),
909        Box::new(step3),
910        Box::new(case4),
911        Box::new(packet_post),
912        Box::new(packet_notif),
913        Box::new(packet),
914    ] {
915        project.scene.add(element);
916    }
917
918    project.show().expect("Failed to render");
919}