Show HN: Breathe CLI – 在终端中进行共振呼吸练习
Show HN: Breathe CLI – Paced resonance breathing in the macOS terminal

原始链接: https://github.com/marekkowalczyk/breathe-cli

这款适用于 macOS 的终端应用程序提供了一个极简、无需依赖项的“共振呼吸”工具。共振呼吸是指以每分钟约 6 次的频率进行呼吸,旨在提升心脏迷走神经张力。通过减缓呼吸速度,该应用能增强呼吸性窦性心律不齐(RSA),帮助将自主神经平衡调节至副交感神经系统,从而有益于心血管健康。 **主要功能:** * **简洁界面:** 直接在终端运行,无需任何外部依赖。 * **临床依据:** 采用心脏研究(如 Bernardi 等人)中经过验证的方案,强调持续呼吸而不屏息,因为屏息对某些患者可能存在风险。 * **自定义预设:** 提供平衡比例(5-5)和呼气侧重比例(4-6),以刺激迷走神经活动。 * **安全至上:** 严格防止过度换气或屏息等危险呼吸模式。 * **个性化设置:** 用户可结合 HRV(心率变异性)监测硬件识别个人的最佳共振频率,并据此调整呼吸比例。 **重要提示:** 本工具并非医疗设备,仅作为习惯培养的辅助手段。用户在使用前应咨询医生,特别是患有既往心脏或呼吸系统疾病的人群。如感到不适,请立即停止使用。

**Breathe CLI** 是一款极简的开源 macOS 终端应用程序,旨在帮助用户进行有节奏的共振呼吸(每分钟 6 次)。该工具由一名心脏病患者开发,通过零摩擦、无干扰的界面,致力于改善迷走神经张力和压力反射敏感度,且无需注册账号、订阅或通过应用商店下载。 该工具仅使用 Python 标准库构建,优先考虑安全性和易用性。它遵循严格的临床指导原则,例如避免屏息和极端的呼吸比例,以确保心脏病患者也可安全使用。该应用程序通过 `afplay` 提供语音引导课程,并包含紧急退出功能以保障用户安全。 该工具设计简洁,可通过 `pip` 或 Homebrew 安装。项目还为用户提供了一套方案,可通过心率变异性(HRV)监测仪确定个人的共振频率,从而实现超越默认每分钟 6 次呼吸的个性化体验。其源代码采用 MIT 许可协议,现已托管于 GitHub。
相关文章

原文

PyPI version Python 3.7+ License: MIT Homebrew

A terminal app that paces resonance breathing for vagal tone training. macOS only, single file, no dependencies.

$ breathe

  calm · 4-6 · 14:32   [●]

                INHALE

          ██████████████░░░░░░░░░░░░░░░░

  space pause · s mute · q quit

Resonance breathing — slow, paced breathing at around 6 breaths per minute — is one of the few non-pharmacological interventions shown to improve cardiac vagal tone. The mechanism is straightforward: slow breathing amplifies respiratory sinus arrhythmia (RSA), the natural heart-rate variation linked to the breath cycle. Stronger RSA means stronger vagal outflow, which in turn improves baroreceptor sensitivity and shifts autonomic balance away from sympathetic dominance.

This matters most for people with heart failure with reduced ejection fraction (HFrEF), where sympathetic overdrive is both a symptom and an accelerant of disease progression. Bernardi et al. (1998) demonstrated that slow breathing at 6 bpm improves oxygen saturation and exercise tolerance in CHF patients, with effects visible after a single session. A follow-up study (Bernardi et al. 2002) showed that slow breathing also increases arterial baroreflex sensitivity in CHF — a marker strongly associated with prognosis.

This app is a habit tool that makes daily practice frictionless: open terminal, run breathe, follow the bar. It is not a medical device.

Why 6 breaths per minute? The cardiovascular system has a resonance frequency — typically between 4.5 and 6.5 bpm in adults — at which heart rate oscillations are maximally amplified (Vaschillo et al. 2006). Breathing at or near this frequency produces the largest RSA swings, which drive the strongest vagal training stimulus. Individual resonance frequency varies and can only be identified precisely with HRV biofeedback hardware. Without it, 6 bpm is the best population-level default: it sits at the centre of the typical range and matches the rate used in the CHF clinical trials (Bernardi et al. 1998, 2002).

Why a longer exhale in the calm and extended presets? Cardiac vagal efferent activity is gated to the respiratory cycle — vagal outflow is stronger during expiration than inspiration. A longer exhale (4s in, 6s out) extends the phase of peak vagal drive within each breath, biasing the autonomic balance further toward parasympathetic tone (Russo et al. 2017, Lehrer & Gevirtz 2014). The total cycle is still 10 seconds (6 bpm). The balanced preset uses equal timing (5-5) as a neutral baseline; the calm and extended presets use the exhale-weighted ratio for parasympathetic emphasis.

Why these safety constraints? See the Design choices section below. Each constraint maps to a specific physiological risk that is elevated in cardiac patients.

Finding your resonance frequency

The presets use 6 bpm because it works well for most people and matches the clinical trial protocols. But individual resonance frequency varies — typically between 4.5 and 6.5 bpm — and breathing at your resonance frequency produces a stronger vagal training stimulus than breathing at the population average (Vaschillo et al. 2006).

If you have HRV biofeedback hardware, you can find your personal optimum. If you don't, the 6 bpm default is a good choice — consistent daily practice matters more than nailing the exact frequency.

  • A chest-strap heart rate monitor (e.g. Polar H10, Garmin HRM-Pro). Wrist-based optical sensors are not accurate enough for beat-to-beat HRV.
  • Software that displays real-time R-R intervals or HRV metrics: Kubios, Elite HRV, HRV4Training, or a dedicated biofeedback system.

Run this test sitting upright in a quiet room, at the same time of day you normally practice. The whole procedure takes about 30 minutes.

  1. Baseline (2 min). Breathe normally. Let your heart rate settle. Start your HRV recording.
  2. Test rate 1: 6.0 bpm (3 min). Run breathe --ratio 5-5 -d 3 --no-log. Follow the pacer. At the end, note the average RMSSD (or, if your software shows a live heart rate trace, note how wide the oscillations are — peak-to-trough in bpm).
  3. Rest (1–2 min). Breathe normally.
  4. Test rate 2: 5.5 bpm (3 min). Run breathe --ratio 5-6 -d 3 --no-log. Note the same metric.
  5. Rest (1–2 min).
  6. Test rate 3: 5.0 bpm (3 min). Run breathe --ratio 6-6 -d 3 --no-log. Note the same metric.
  7. Rest (1–2 min).
  8. Test rate 4: 4.6 bpm (3 min). Run breathe --ratio 6-7 -d 3 --no-log. Note the same metric.

Interpreting results: The rate that produces the highest RMSSD, the highest LF power in the HRV spectrum, or the visibly widest heart rate oscillations is your resonance frequency. If two adjacent rates are close, pick the slower one — it's more comfortable for long sessions.

Limitations: Phase durations are whole seconds, so only certain BPMs are representable: 4.6, 5.0, 5.5, 6.0, 6.7, 7.5 bpm. Your true resonance might fall between two testable rates. Pick the closest one. The difference in training effect between 5.0 and 5.5 bpm is small.

Once you know your frequency, use --ratio to match it:

breathe --ratio 6-7    # 13s cycle = 4.6 bpm
breathe --ratio 6-6    # 12s cycle = 5.0 bpm
breathe --ratio 5-6    # 11s cycle = 5.5 bpm
breathe --ratio 5-5    # 10s cycle = 6.0 bpm (default)

You can also add exhale emphasis at your resonance frequency:

breathe --ratio 5-7    # 12s cycle = 5.0 bpm, exhale-weighted
breathe --ratio 4-7    # 11s cycle = 5.5 bpm, exhale-weighted
breathe --ratio 4-8    # 12s cycle = 5.0 bpm, strong exhale emphasis

This app is deliberately constrained. Several common breathing-app features are excluded for safety and focus:

No breath retention. Breath holds (kumbhaka) raise intrathoracic pressure via a Valsalva-like mechanism and can trigger vasovagal syncope or arrhythmia in cardiac patients. The Bernardi protocols use continuous breathing with no hold phases. The app rejects three-number ratios like 4-7-8 with an explicit safety error.

No rapid breathing. Patterns faster than 7.5 bpm (cycles shorter than 8 seconds) move toward hyperventilation territory, reducing arterial CO2 and mobilising catecholamines — the opposite of the vagal intent (Russo et al. 2017). The app enforces a minimum cycle length of 8 seconds.

No breath holds between phases. There is no pause between inhale and exhale. The breath is continuous, matching the protocol in Bernardi et al. (1998, 2002).

Immediate exit, always. Pressing q or Ctrl+C ends the session within one frame. The terminal is always restored — cursor, colours, input mode — even if the app crashes. The finally block that does this is the most important code in the file.

No dependencies. Single Python file, stdlib only. Nothing to install, nothing to break. Runs on the Python that ships with macOS.

No curses. Direct ANSI escape codes only. The curses library has edge cases with non-default terminals on macOS Mojave.

  • macOS (uses /usr/bin/afplay for audio cues)
  • Python 3.7+
# Clone or download breathe.py, then:
chmod +x breathe.py

# Option A: run directly
./breathe.py

# Option B: symlink into your PATH
ln -s "$(pwd)/breathe.py" /usr/local/bin/breathe
breathe

No arguments — time-of-day auto-select

With no arguments, the app picks a preset based on the time of day:

Time of day Preset Duration Ratio BPM
Before noon balanced 10 min 5s-5s 6
12:00–16:59 extended 20 min 4s-6s 6
17:00+ calm 15 min 4s-6s 6

All presets target 6 breaths per minute. The balanced preset uses equal inhale/exhale (5-5) as a neutral baseline. The calm and extended presets use a longer exhale (4-6), which emphasises vagal activation during the expiratory phase. The time-of-day auto-select picks calm in the evening as a default — but you can use any preset at any time.

breathe --preset balanced    # 10 min, 5s-5s
breathe --preset calm        # 15 min, 4s-6s
breathe --preset extended    # 20 min, 4s-6s (full Bernardi protocol dose)
breathe --list-presets        # show the table
breathe --duration 5                # 5 minutes, default 5-5 ratio
breathe --ratio 4-6                 # default 10 minutes, 4-6 ratio
breathe --duration 12 --ratio 4-6   # 12 minutes, 4-6 ratio

Duration: 1–60 minutes (rounded up to complete breath cycles). Ratio: inhale and exhale each 3–10 seconds, total cycle >= 8 seconds, exhale at most 2x inhale.

Flag Short Description
--preset NAME -p Use a named preset
--duration MIN -d Session length in minutes (1–60)
--ratio IN-EX -r Breath ratio, e.g. 5-5 or 4-6
--no-sound -n Disable audio cues
--quiet -q Suppress startup warnings
--no-log Don't log this session
--log Print log file path and exit
--safety Print safety information and exit
--list-presets Print preset table and exit
--version Print version and exit

During a session:

Key Action
space Pause / resume. Resume restarts from the beginning of INHALE.
s Toggle sound mute.
q Quit immediately. Terminal is restored.
Ctrl+C Same as q.
  balanced · 5-5 · 09:12   [●]       <- preset, ratio, countdown, status

                INHALE                <- current phase (cyan) or EXHALE (green)

          ████████████████░░░░░░░░░░░░░░  <- breath bar (fills on inhale, drains on exhale)

  space pause · s mute · q quit       <- available controls

The status indicator shows during breathing, when paused, and 🔇 when muted.

The countdown timer tracks completed breathing time only. If you pause for 30 seconds during a 1-minute session, the session takes ~90 seconds of wall-clock time to complete — the timer doesn't advance while paused.

Each session appends a row to ~/.breathe_log.csv:

date,time,preset,ratio,duration_target_s,duration_actual_s,breaths,completion_pct,status
2026-05-30,07:15:02,balanced,5-5,600,600,60,100,completed
2026-05-30,19:30:14,calm,4-6,900,420,42,46,ended early (user)

Use --no-log to skip logging for a session. Use --log to see the log file path.

Automated tests cover logic and arithmetic (formatting, ratio parsing, safety rejections, preset invariants, countdown calculation):

python3 -m unittest test_breathe -v

TUI behaviour (rendering, animation, terminal restoration) is covered by 25 manual acceptance tests in dev/breathe-cli-spec.md.

Run breathe --safety for the full safety screen. The short version:

Stop immediately if you experience lightheadedness, palpitations, or tingling in your hands or face.

This app deliberately does not support breath retention, rapid breathing, or any pattern not grounded in the slow-breathing clinical literature. These constraints are enforced in the code and cannot be overridden. See The science in brief and Design choices for the clinical rationale.

This app is not a medical device. It does not diagnose, treat, cure, or prevent any disease or condition. Always consult your physician before starting a breathing practice, especially if you have a cardiac or respiratory condition. Use entirely at your own risk. The author assumes no liability for any adverse effects arising from the use of this software. By using this app you acknowledge that you understand and accept these terms.

MIT License. See LICENSE for the full text.

Created by Marek Kowalczyk.

联系我们 contact @ memedata.com