diff --git a/docs/api/sensor_streams/advanced_streams.md b/docs/api/sensor_streams/advanced_streams.md
index 2ab6f04725..187d432af2 100644
--- a/docs/api/sensor_streams/advanced_streams.md
+++ b/docs/api/sensor_streams/advanced_streams.md
@@ -122,14 +122,34 @@ class MLModel(Module):
```
+## Getting Values Synchronously
+Sometimes you don't want a stream, you just want to call a function and get the latest value.
+If you are doing this periodically as a part of a processing loop, it is very likely that your code will be much cleaner and safer using actual reactivex pipeline. So bias towards checking our [reactivex quick guide](reactivex.md) and [official docs](https://rxpy.readthedocs.io/)
+(TODO we should actually make this example actually executable)
+```python skip
+ self.color_image.observable().pipe(
+ # takes the best image from a stream every 200ms,
+ # ensuring we are feeding our detector with highest quality frames
+ quality_barrier(lambda x: x["quality"], target_frequency=0.2),
-## Getting Values Synchronously
+ # converts Image into Person detections
+ ops.map(detect_person),
+
+ # converts Detection2D to Twist pointing in the direction of a detection
+ ops.map(detection2d_to_twist),
+
+ # emits the latest value every 50ms making our control loop run at 20hz
+ # despite detections running at 200ms
+ ops.sample(0.05),
+ ).subscribe(self.twist.publish) # shoots off the Twist out of the module
+```
-Sometimes you don't want a stream, you just want to call a function and get the latest value. We provide two approaches:
+
+If you'd still like to switch to synchronous fetching, we provide two approaches, `getter_hot()` and `getter_cold()`
| | `getter_hot()` | `getter_cold()` |
|------------------|--------------------------------|----------------------------------|
@@ -138,6 +158,85 @@ Sometimes you don't want a stream, you just want to call a function and get the
| **Resources** | Keeps connection open | Opens/closes each call |
| **Use when** | Frequent reads, need latest | Occasional reads, save resources |
+
+diagram source
+
+```pikchr fold output=assets/getter_hot_cold.svg
+color = white
+fill = none
+
+H_Title: box "getter_hot()" rad 5px fit wid 170% ht 170%
+
+Sub: box "subscribe" rad 5px fit wid 170% ht 170% with .n at H_Title.s + (0, -0.5in)
+arrow from H_Title.s to Sub.n
+arrow right from Sub.e
+Cache: box "Cache" rad 5px fit wid 170% ht 170%
+
+# blocking box around subscribe->cache (one-time setup)
+Blk0: box dashed color 0x5c9ff0 with .nw at Sub.nw + (-0.1in, 0.25in) wid (Cache.e.x - Sub.w.x + 0.2in) ht 0.7in rad 5px
+text "blocking" italic with .n at Blk0.n + (0, -0.05in)
+
+arrow right from Cache.e
+Getter: box "getter" rad 5px fit wid 170% ht 170%
+
+arrow from Getter.e right 0.3in then down 0.25in then right 0.2in
+G1: box invis "call()" color 0x8cbdf2 fit wid 150%
+arrow right 0.4in from G1.e
+box invis "instant" fit wid 150%
+
+arrow from Getter.e right 0.3in then down 0.7in then right 0.2in
+G2: box invis "call()" color 0x8cbdf2 fit wid 150%
+arrow right 0.4in from G2.e
+box invis "instant" fit wid 150%
+
+text "always subscribed" italic with .n at Blk0.s + (0, -0.1in)
+
+
+# === getter_cold section ===
+C_Title: box "getter_cold()" rad 5px fit wid 170% ht 170% with .nw at H_Title.sw + (0, -1.6in)
+
+arrow down 0.3in from C_Title.s
+ColdGetter: box "getter" rad 5px fit wid 170% ht 170%
+
+# Branch to first call
+arrow from ColdGetter.e right 0.3in then down 0.3in then right 0.2in
+Cold1: box invis "call()" color 0x8cbdf2 fit wid 150%
+arrow right 0.4in from Cold1.e
+Sub1: box invis "subscribe" fit wid 150%
+arrow right 0.4in from Sub1.e
+Wait1: box invis "wait" fit wid 150%
+arrow right 0.4in from Wait1.e
+Val1: box invis "value" fit wid 150%
+arrow right 0.4in from Val1.e
+Disp1: box invis "dispose " fit wid 150%
+
+# blocking box around first row
+Blk1: box dashed color 0x5c9ff0 with .nw at Cold1.nw + (-0.1in, 0.25in) wid (Disp1.e.x - Cold1.w.x + 0.2in) ht 0.7in rad 5px
+text "blocking" italic with .n at Blk1.n + (0, -0.05in)
+
+# Branch to second call
+arrow from ColdGetter.e right 0.3in then down 1.2in then right 0.2in
+Cold2: box invis "call()" color 0x8cbdf2 fit wid 150%
+arrow right 0.4in from Cold2.e
+Sub2: box invis "subscribe" fit wid 150%
+arrow right 0.4in from Sub2.e
+Wait2: box invis "wait" fit wid 150%
+arrow right 0.4in from Wait2.e
+Val2: box invis "value" fit wid 150%
+arrow right 0.4in from Val2.e
+Disp2: box invis "dispose " fit wid 150%
+
+# blocking box around second row
+Blk2: box dashed color 0x5c9ff0 with .nw at Cold2.nw + (-0.1in, 0.25in) wid (Disp2.e.x - Cold2.w.x + 0.2in) ht 0.7in rad 5px
+text "blocking" italic with .n at Blk2.n + (0, -0.05in)
+```
+
+
+
+
+
+
+
**Prefer `getter_cold()`** when you can afford to wait and warmup isn't expensive. It's simpler (no cleanup needed) and doesn't hold resources. Only use `getter_hot()` when you need instant reads or the source is expensive to start.
### `getter_hot()` - Background subscription, instant reads
diff --git a/docs/api/sensor_streams/assets/getter_hot_cold.svg b/docs/api/sensor_streams/assets/getter_hot_cold.svg
new file mode 100644
index 0000000000..d2f336459c
--- /dev/null
+++ b/docs/api/sensor_streams/assets/getter_hot_cold.svg
@@ -0,0 +1,71 @@
+