OpenScreen 深度解析:当开源撕掉屏幕录制的高价标签,开发者终于不用再被 Screen Studio 收割
背景介绍:当「录制」变成一种奢侈消费
做技术内容创作的同学,大概都绕不开一个痛点——录屏。
你想录一段代码演示给团队新人,想录一个产品功能介绍放到官网,想录一段技术分享发到 B 站,或者只是想把自己解决问题的过程记录下来以后复盘——这些场景太常见了,需求太普遍了。
但当你真的去找工具的时候,你会发现一个诡异的现象:「录屏」这件事,在 2026 年的今天,居然还是个付费生意。
最典型的例子就是 Screen Studio。这个 macOS 上的明星录屏工具,定价 $29/月 或者 $89 买断。光看这个价格你可能会想:它凭什么?
凭的是它能做到的效果:自动缩放(录制时光标走到哪里,镜头就自动跟到哪里)、运动模糊(光标移动时带有自然的拖影效果)、专业的背景虚化、以及导出即用的高质量视频。确实,比 macOS 自带的 QuickTime 或者 OBS Studio 强了不止一个档次。
问题是:$89 买断的价格,对于一个开源社区来说,完全可以做出来。
然后 Siddharth Vaddem 站出来了。
2026 年初,他开源了一个项目叫 OpenScreen,定位就是 Screen Studio 的免费开源替代品。项目上线 GitHub 后迅速走红,目前已经积累 26,625+ Stars,本周新增超过 13,938 Stars,稳居 GitHub Trending 前列。
这就是今天这篇文章的主角。
核心概念:OpenScreen 是什么
项目定位
OpenScreen 的官方定义非常清晰:
一款免费的屏幕录制与演示视频创作工具,是付费软件 Screen Studio 的开源替代品。完全免费、无订阅、无水印、可商用。
技术栈:Electron + React + TypeScript。
这意味着它是一个跨平台的桌面应用,基于 Web 技术构建,分发到 macOS、Windows 和 Linux 上运行。
目标用户
OpenScreen 的目标用户非常明确:开发者和技术内容创作者。
不是给影视制作人用的,不是给游戏主播用的。它的核心场景是:
- 录制代码演示和技术讲解视频
- 制作产品功能介绍和教程
- 录制 Bug 修复过程用于知识沉淀
- 为开源项目制作演示视频
这些场景有一个共同特点:录制的画面主要是屏幕内容(代码、UI),偶尔有摄像头画中画,对自动缩放和光标美化有强需求,但不需要 Premiere 级别的专业剪辑能力。
与竞品的核心差异
我们先来看一张对比表,直观看差异:
| 维度 | OpenScreen | Screen Studio | OBS Studio | Camtasia |
|---|---|---|---|---|
| 价格 | 免费开源 | $89 买断 / $29 月 | 免费开源 | $249.5 买断 |
| 自动缩放 | ✅ | ✅ | ❌ | ❌ |
| 运动模糊 | ✅ | ✅ | ❌ | ❌ |
| 背景虚化 | ✅ | ✅ | ❌ | ❌ |
| 开箱即用 | ✅ 开箱即用 | ✅ 开箱即用 | ❌ 需配置 | ✅ 开箱即用 |
| 技术栈 | Electron | 原生 macOS | C/C++ | 原生 |
| 跨平台 | ✅ 全平台 | ❌ 仅 macOS | ✅ 全平台 | ✅ 全平台 |
最直接的差异化:Screen Studio 能做到的效果,OpenScreen 全部有,而且免费,而且跨平台。
架构分析:Electron 桌面应用的技术选型逻辑
为什么是 Electron?
这个问题很关键。用原生开发(Swift/Kotlin)当然也能做录屏,但成本高、维护难、跨平台要写两套代码。
用 Electron 做录屏,有几个天然优势:
1. 屏幕捕获 API 是 Web API
Electron 基于 Chromium,而 Chromium 内置了 desktopCapturer API,这是 Chromium 团队为 WebRTC 场景开发的屏幕捕获实现,封装成了可以直接调用的 JavaScript API:
// Electron 中获取可录制的窗口和屏幕
const { desktopCapturer } = require('electron');
async function getScreenSources() {
const sources = await desktopCapturer.getSources({
types: ['window', 'screen'],
thumbnailSize: { width: 320, height: 180 }
});
return sources.map(source => ({
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL()
}));
}
这套 API 在 Electron 12+ 版本中已经相当成熟,直接拿到屏幕画面帧数据,不需要额外调用系统底层 API。
2. FFmpeg 是最好的视频处理库,而 FFmpeg 有成熟的 Node.js 绑定
录屏拿到的是原始视频帧,要变成可以导出的 MP4/GIF,需要编码和封装。FFmpeg 是这个领域当之无愧的王者,而 Node.js 生态中有 fluent-ffmpeg 和 @ffmpeg/ffmpeg 等成熟绑定,Electron 可以无缝集成。
// 使用 fluent-ffmpeg 进行视频编码
const ffmpeg = require('fluent-ffmpeg');
function encodeVideo(inputPath, outputPath, options = {}) {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.outputOptions([
'-c:v libx264', // H.264 编码
'-preset medium', // 编码速度/质量平衡
'-crf 23', // 质量参数 (0-51, 越小越好)
'-pix_fmt yuv420p', // 兼容所有播放器
`-vf scale=${options.width || 1920}:${options.height || 1080}`
])
.audioCodec('aac')
.audioBitrate('128k')
.output(outputPath)
.on('end', resolve)
.on('error', reject)
.run();
});
}
3. React 负责 UI,TypeScript 确保代码质量
录屏应用的 UI 相对固定(录制控制栏、时间显示、预览窗口、导出设置),用 React 做状态管理和组件化非常合适。TypeScript 的静态类型检查在 Electron 的 IPC 通信中尤为重要——主进程和渲染进程之间的数据传递,如果类型错了,轻则功能异常,重则崩溃。
OpenScreen 的架构分层
从 GitHub 上的源码结构来看,OpenScreen 采用了典型的 Electron 三层架构:
┌─────────────────────────────────────────────────────────┐
│ Renderer Process │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ React UI │ │ State Manager │ │ Video Preview │ │
│ │ (Controls) │ │ (Recording) │ │ (Canvas) │ │
│ └─────────────┘ └──────────────┘ └────────────────┘ │
│ │ IPC │
├─────────────────────────┼───────────────────────────────┤
│ Main Process │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ Capture │ │ FFmpeg │ │ File System │ │
│ │ Manager │ │ Encoder │ │ Manager │ │
│ └─────────────┘ └──────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────┘
- 捕获层(Capture Manager):调用
desktopCapturer获取屏幕/窗口帧数据,管理录制生命周期 - 编码层(FFmpeg Encoder):接收原始帧序列,执行 H.264/AAC 编码和 MP4 封装
- 文件管理层(File System Manager):管理临时文件和最终导出,处理文件系统权限
代码实战:从零理解屏幕录制核心流程
第一步:获取可录制的屏幕源
在 Electron 中,录制之前要先问系统:「有哪些窗口和屏幕可以录?」这通过 desktopCapturer.getSources() 实现:
// main-process/captureManager.js
const { desktopCapturer } = require('electron');
class CaptureManager {
constructor() {
this.currentStream = null;
this.recording = false;
}
// 获取所有可用的录制源
async getAvailableSources() {
const sources = await desktopCapturer.getSources({
types: ['window', 'screen'], // 同时获取窗口和屏幕
thumbnailSize: { width: 320, height: 180 },
fetchWindowIcons: true
});
return sources.map(source => ({
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL(),
isScreen: source.id.startsWith('screen:'),
isWindow: source.id.startsWith('window:')
}));
}
// 选择一个源开始预览
async startPreview(sourceId) {
// 获取该源的媒体流约束
const constraints = {
audio: false, // 预览时不需要音频
video: {
// sourceId 格式: "window:12345:0" 或 "screen:1:0"
mandatory: {
chromeMediaSource: sourceId.split(':')[0],
chromeMediaSourceId: sourceId
}
}
};
try {
this.currentStream = await navigator.mediaDevices.getUserMedia(constraints);
return this.currentStream;
} catch (err) {
console.error('获取媒体流失败:', err);
throw err;
}
}
}
module.exports = new CaptureManager();
第二步:实现带音频的录制
录制时需要同时捕获屏幕画面和系统音频(或者麦克风声音)。这里用到的是 MediaRecorder API:
// renderer-process/recorder.js
class ScreenRecorder {
constructor() {
this.mediaRecorder = null;
this.recordedChunks = [];
this.audioContext = null;
}
// 组合屏幕视频流 + 音频流
async setupStream(screenStream, audioSource = 'system') {
// audioSource: 'system' = 系统音频, 'microphone' = 麦克风, 'both' = 两者
const audioConstraints = { audio: audioSource === 'system' ? true : 'microphone' };
let audioStream;
if (audioSource !== 'none') {
try {
// macOS: 需要在 Electron 中配置音频捕获权限
audioStream = await navigator.mediaDevices.getUserMedia(audioConstraints);
} catch (e) {
console.warn('音频捕获失败,继续仅录屏:', e);
}
}
// 使用 canvas 合并视频和音频轨道
if (audioStream) {
const combinedStream = new MediaStream();
// 添加视频轨道
screenStream.getVideoTracks().forEach(track => {
combinedStream.addTrack(track);
});
// 添加音频轨道
audioStream.getAudioTracks().forEach(track => {
combinedStream.addTrack(track);
});
return combinedStream;
}
return screenStream;
}
// 开始录制
startRecording(stream, options = {}) {
this.recordedChunks = [];
// 视频编码选项
const mimeType = MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
? 'video/webm;codecs=vp9'
: 'video/webm;codecs=vp8';
this.mediaRecorder = new MediaRecorder(stream, {
mimeType,
videoBitsPerSecond: options.videoQuality === 'high' ? 8000000 : 4000000,
audioBitsPerSecond: 128000
});
this.mediaRecorder.ondataavailable = (event) => {
if (event.data && event.data.size > 0) {
this.recordedChunks.push(event.data);
}
};
this.mediaRecorder.start(1000); // 每 1 秒生成一个数据块
console.log('录制已开始');
}
// 停止录制并返回 Blob
async stopRecording() {
return new Promise((resolve) => {
this.mediaRecorder.onstop = () => {
const blob = new Blob(this.recordedChunks, { type: 'video/webm' });
resolve(blob);
};
this.mediaRecorder.stop();
});
}
}
module.exports = ScreenRecorder;
第三步:实现自动缩放(Auto-Zoom)
自动缩放是 OpenScreen 和 Screen Studio 的核心差异点。它的原理是:追踪光标位置,动态调整 Canvas 视图的缩放比例和中心点。
// renderer-process/autoZoom.js
class AutoZoom {
constructor(canvas, videoElement) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.video = videoElement;
this.zoom = 1.0; // 当前缩放级别
this.targetX = 0; // 目标中心 X
this.targetY = 0; // 目标中心 Y
this.smoothing = 0.1; // 平滑系数(越小越平滑)
this.enabled = true;
this.zoomOnActivity = true; // 移动时触发缩放
this.minZoom = 1.0;
this.maxZoom = 2.5;
this.zoomRadius = 150; // 光标周围的有效缩放区域(像素)
// 鼠标追踪
document.addEventListener('mousemove', (e) => this.onMouseMove(e));
// 开始渲染循环
this.renderLoop();
}
onMouseMove(event) {
if (!this.enabled || !this.zoomOnActivity) return;
const rect = this.canvas.getBoundingClientRect();
// 计算光标相对于 Canvas 的位置
const mouseX = (event.clientX - rect.left) * (this.canvas.width / rect.width);
const mouseY = (event.clientY - rect.top) * (this.canvas.height / rect.height);
// 根据光标位置计算需要的缩放
// 光标越靠近边缘,缩放越大
const centerX = this.canvas.width / 2;
const centerY = this.canvas.height / 2;
const distFromCenter = Math.sqrt(
Math.pow(mouseX - centerX, 2) +
Math.pow(mouseY - centerY, 2)
);
// 将距离映射为缩放值
const maxDist = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
const normalizedDist = distFromCenter / maxDist;
// 距离中心越远 → 缩放越大
const targetZoom = this.minZoom + (this.maxZoom - this.minZoom) * Math.pow(normalizedDist, 2);
// 平滑过渡到目标缩放
this.zoom = this.zoom + (targetZoom - this.zoom) * this.smoothing;
// 目标中心点向光标方向偏移
this.targetX = mouseX;
this.targetY = mouseY;
}
renderLoop() {
const render = () => {
if (!this.enabled) {
requestAnimationFrame(render);
return;
}
const w = this.canvas.width;
const h = this.canvas.height;
// 清空画布
this.ctx.clearRect(0, 0, w, h);
// 保存状态
this.ctx.save();
// 计算裁剪区域(以 targetX, targetY 为中心)
const cropW = w / this.zoom;
const cropH = h / this.zoom;
const cropX = this.targetX - cropW / 2;
const cropY = this.targetY - cropH / 2;
// 绘制缩放后的视频帧
this.ctx.drawImage(
this.video,
cropX, cropY, cropW, cropH, // 源区域
0, 0, w, h // 目标区域
);
// 恢复状态
this.ctx.restore();
requestAnimationFrame(render);
};
render();
}
// 切换自动缩放开关
toggle() {
this.enabled = !this.enabled;
return this.enabled;
}
}
module.exports = AutoZoom;
这段代码的核心逻辑:
- 监听鼠标移动事件,获取光标在 Canvas 上的相对坐标
- 计算光标距离 Canvas 中心的距离
- 距离越远 → 缩放值越大(最大 2.5 倍)
- 使用
smoothing系数做平滑插值,避免画面抖动
第四步:运动模糊效果
运动模糊(Motion Blur)是在光标移动时给轨迹添加渐变拖影,让画面看起来更自然。实现思路是:每帧不完全清除画布,而是用半透明黑色覆盖,造成拖影效果。
// renderer-process/motionBlur.js
class MotionBlur {
constructor(ctx) {
this.ctx = ctx;
this.trail = []; // 存储历史鼠标位置
this.maxTrailLength = 10; // 拖影长度
this.opacity = 0.3; // 拖影透明度
}
// 记录新的光标位置
addPosition(x, y) {
this.trail.push({ x, y, timestamp: Date.now() });
// 限制历史长度
if (this.trail.length > this.maxTrailLength) {
this.trail.shift();
}
}
// 渲染运动模糊(覆盖在每帧画面之上)
render(canvasWidth, canvasHeight) {
if (this.trail.length < 2) return;
const ctx = this.ctx;
// 从旧到新绘制拖影
for (let i = 0; i < this.trail.length - 1; i++) {
const point = this.trail[i];
const nextPoint = this.trail[i + 1];
// 越新的位置透明度越高
const progress = i / this.trail.length;
const alpha = this.opacity * (1 - progress);
// 绘制从 point 到 nextPoint 的渐变线
const gradient = ctx.createLinearGradient(
point.x, point.y,
nextPoint.x, nextPoint.y
);
gradient.addColorStop(0, `rgba(0, 0, 0, ${alpha * 0.3})`);
gradient.addColorStop(1, `rgba(0, 0, 0, ${alpha})`);
ctx.beginPath();
ctx.strokeStyle = gradient;
ctx.lineWidth = 4 * (1 - progress * 0.5); // 越远越细
ctx.lineCap = 'round';
ctx.moveTo(point.x, point.y);
ctx.lineTo(nextPoint.x, nextPoint.y);
ctx.stroke();
}
// 绘制当前光标位置的光晕
const current = this.trail[this.trail.length - 1];
const gradient = ctx.createRadialGradient(
current.x, current.y, 0,
current.x, current.y, 15
);
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.8)');
gradient.addColorStop(0.3, 'rgba(255, 255, 255, 0.3)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.beginPath();
ctx.fillStyle = gradient;
ctx.arc(current.x, current.y, 15, 0, Math.PI * 2);
ctx.fill();
}
// 清除历史(开始新录制时调用)
clear() {
this.trail = [];
}
}
module.exports = MotionBlur;
第五步:FFmpeg 后处理导出
录屏完成后,浏览器原生的 WebM 格式虽然可以直接播放,但为了更好的兼容性,需要用 FFmpeg 转码为 MP4,并且加上水印、裁剪等后处理:
// main-process/ffmpegProcessor.js
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
class VideoProcessor {
constructor() {
// 设置 FFmpeg 路径(Electron 打包后需要指定)
ffmpeg.setFfmpegPath('/usr/local/bin/ffmpeg'); // macOS 默认路径
}
// 将 WebM 转换为 MP4
async convertToMP4(inputPath, outputPath) {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.outputOptions([
'-c:v libx264', // H.264 视频编码
'-preset fast', // 快速编码(速度优先)
'-crf 20', // 高质量 (0-51, 越小越好)
'-c:a aac', // AAC 音频编码
'-b:a 192k', // 192kbps 音频
'-movflags +faststart', // 优化 Web 播放
'-pix_fmt yuv420p' // 确保兼容性
])
.output(outputPath)
.on('progress', (progress) => {
console.log(`处理进度: ${progress.percent?.toFixed(1)}%`);
})
.on('end', () => {
console.log('转码完成:', outputPath);
resolve(outputPath);
})
.on('error', (err) => {
console.error('转码失败:', err);
reject(err);
})
.run();
});
}
// 添加自动缩放效果(使用 FFmpeg zoompan 滤镜)
async applyAutoZoomEffect(inputPath, outputPath, zoomPath) {
// zoomPath 是 JSON 配置文件,记录了每帧的缩放参数
return new Promise((resolve, reject) => {
const zoomConfig = require(zoomPath);
// 构建 zoompan 滤镜参数
// 格式: zoompan=z='min(zoom+0.001,2)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)'
// 这里用 zoomConfig 动态生成每帧的参数
ffmpeg(inputPath)
.videoFilters(`zoompan=z='min(zoom+0.002,2.5)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=${1920}x${1080}`)
.outputOptions([
'-c:v libx264',
'-preset fast',
'-crf 20',
'-pix_fmt yuv420p'
])
.output(outputPath)
.on('end', () => resolve(outputPath))
.on('error', (err) => reject(err))
.run();
});
}
// 导出为 GIF(用于社交媒体)
async exportToGIF(inputPath, outputPath, options = {}) {
const width = options.width || 480;
const fps = options.fps || 10;
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.outputOptions([
`-vf scale=${width}:-1:flags=lanczos,fps=${fps}`
])
.format('gif')
.output(outputPath)
.on('end', () => resolve(outputPath))
.on('error', (err) => reject(err))
.run();
});
}
}
module.exports = new VideoProcessor();
性能优化:Electron 录屏应用的优化策略
1. 录制帧率的智能选择
不是所有场景都需要 60fps。对于代码演示类内容,30fps 足够,但可以省一半的存储和处理开销。OpenScreen 提供了帧率选项:
// 根据录制内容类型自动选择帧率
function selectOptimalFrameRate(contentType) {
switch (contentType) {
case 'gaming':
return 60; // 游戏需要高帧率
case 'code-demo':
return 30; // 代码演示 30fps 足够
case 'slow-motion':
return 120; // 慢动作回放
default:
return 30;
}
}
2. 使用 SharedArrayBuffer 减少内存拷贝
Electron 12+ 支持 SharedArrayBuffer,允许渲染进程和主进程共享内存缓冲区,避免大量数据通过 IPC 传递时的序列化开销:
// 在主进程中创建共享缓冲区
const { desktopCapturer } = require('electron');
async function createSharedCapture() {
const { width, height } = { width: 1920, height: 1080 };
// 创建共享缓冲区(RGBA 格式)
const sharedBuffer = new SharedArrayBuffer(width * height * 4);
// 开始捕获到共享内存
// ... 捕获逻辑使用 sharedBuffer 直接写入,避免 IPC 拷贝
return sharedBuffer;
}
3. 录制时使用低分辨率预览
录制可以用高分辨率,但预览窗口可以用低分辨率渲染,减少 GPU 开销:
// 录制使用全分辨率
const highResStream = await navigator.mediaDevices.getUserMedia({
video: { width: 1920, height: 1080, frameRate: 30 }
});
// 预览用低分辨率(Canvas 缩小渲染)
previewCanvas.width = 640;
previewCanvas.height = 360;
// drawImage 会自动缩放,高分辨率 → 低分辨率开销很小
ctx.drawImage(highResVideo, 0, 0, 640, 360);
4. FFmpeg 编码的参数调优
对于不同场景,FFmpeg 的编码参数需要权衡速度和文件大小:
// 速度优先(适合预览)
const fastPreset = ['-preset ultrafast', '-crf 28'];
// 质量优先(适合最终导出)
const qualityPreset = ['-preset slow', '-crf 18'];
// 平衡模式
const balancedPreset = ['-preset medium', '-crf 23'];
// H.264 编码器选择:libx264(兼容性最好)vs libx264 (CPU) vs h264_videotoolbox(macOS GPU 硬解)
function selectEncoder(platform) {
if (platform === 'darwin') {
return 'h264_videotoolbox'; // macOS 使用 VideoToolbox 硬件编码
}
return 'libx264'; // 其他平台用 CPU 编码
}
生态对比:GitHub 上四个同名 OpenScreen 的区别
文章开头的搜索结果中提到,GitHub 上有多个叫 OpenScreen 的项目,极易混淆。在写代码之前,厘清它们的区别很重要:
1. siddharthvaddem/openscreen(⭐ 26,625+)— 本文的主角
免费录屏工具,Screen Studio 开源替代品。
2. chromium/openscreen(Google 官方)
这是 Google 主导的投屏协议库,用 C++ 实现,运行在 Chromium 内部。它实现的是 Open Screen Protocol(OSP),用于 Chromecast 和其他投屏设备之间的通信。名字相同但完全不是一回事。
3. Recordly(openscreen fork)
从 siddharthvaddem/openscreen Fork 出来的社区分支,增加了自动缩放、动态光标模糊、摄像头叠加、自动字幕等进阶功能,适合更专业的产品演示制作。
4. openscreenPlus(中文社区 Fork)
在原版基础上添加了中英文界面自动切换,针对中文用户做了本地化优化。
一句话总结:如果你想录屏,就用 siddharthvaddem/openscreen;如果想做投屏协议开发,就用 chromium/openscreen。
安装与使用
快速安装
macOS(推荐使用 Homebrew)
brew install --cask openscreen
或者从 GitHub Releases 下载
# 假设最新版本是 v1.2.3
curl -L https://github.com/siddharthvaddem/openscreen/releases/download/v1.2.3/OpenScreen-1.2.3.dmg -o openscreen.dmg
open openscreen.dmg
# 拖拽到 Applications 文件夹即可
Linux(AppImage)
wget https://github.com/siddharthvaddem/openscreen/releases/download/v1.2.3/OpenScreen-1.2.3.AppImage
chmod +x OpenScreen-1.2.3.AppImage
./OpenScreen-1.2.3.AppImage
从源码编译
# 克隆项目
git clone https://github.com/siddharthvaddem/openscreen.git
cd openscreen
# 安装依赖
npm install
# 开发模式运行
npm run dev
# 生产构建
npm run build
核心使用流程
- 选择录制源:启动后选择「全屏录制」或「窗口录制」
- 配置录制选项:设置帧率(30fps/60fps)、音频来源(系统音频/麦克风/无)
- 开启自动效果:勾选「自动缩放」「运动模糊」「背景虚化」
- 开始录制:点击录制按钮,3-2-1 倒计时后开始
- 结束录制:点击停止,弹出导出设置
- 导出:选择格式(MP4/GIF/WebM)、分辨率、质量
总结:开源工具如何重塑付费市场
OpenScreen 的出现不是偶然。它代表了开源社区对「高价工具」的一次精准打击。
Screen Studio $89 的定价,对于个人开发者来说不算贵,但不合理。它的核心功能——自动缩放、运动模糊、背景虚化——在底层实现上并不复杂,一个有经验的 Web 开发者完全可以在 Electron 生态里复现出来。
OpenScreen 正是这么做的。它用 Electron + React + FFmpeg 的技术组合,在不到一年的时间内做出了 Screen Studio 80% 的功能,而价格是 $0。
对于我们这些技术内容创作者来说,这是个好消息。工具成本降低,意味着更多人可以参与到技术内容的创作中来。录屏不再是设计师和专业视频制作人的专属,开发者自己就能搞定产品演示、教程录制和 Bug 复盘。
当然,OpenScreen 目前还有不足:自动缩放算法不够细腻、转码速度比 Screen Studio 慢、macOS 上偶尔有音频同步问题。但这些都是开源项目的成长过程。关键在于它证明了:付费工具能做到的,开源一样能做到,而且会越来越接近。
这就是开源的力量。
项目信息
- GitHub: siddharthvaddem/openscreen
- Stars: 26,625+
- 语言: TypeScript
- 技术栈: Electron + React + FFmpeg
- 许可: MIT
- 平台: macOS / Windows / Linux