News & RSS Ticker
Fetch and parse RSS headlines, clean their text, and display them as sequential marquees. The demo feed keeps this source runnable without a network request.

Complete app
app.star
load("bsoup.star", "bsoup")
load("html.star", "html")
load("http.star", "http")
load("re.star", "re")
load("render.star", "render")
load("schema.star", "schema")
load("xpath.star", "xpath")
DEMO_MODE = True
DEMO_RSS = """
<rss><channel>
<item><title>Pixel displays brighten the morning commute</title></item>
<item><title>Developers ship a tiny weather dashboard</title></item>
</channel></rss>
"""
def fetch_feed():
response = http.get("https://news.example/feed.xml", ttl_seconds = 900)
if response.status_code != 200:
fail("Feed request failed: %d" % response.status_code)
return response.body()
def source_name():
markup = "<header><span class='source'>PIXEL WIRE</span></header>"
return html(markup).find(".source").text()
def source_kicker():
soup = bsoup.parseHtml("<p><strong>LIVE</strong> updates</p>")
return soup.find("strong").get_text()
def main():
feed = DEMO_RSS if DEMO_MODE else fetch_feed()
titles = xpath.loads(feed).query_all("/rss/channel/item/title")
clean_titles = [re.sub(r"\s+", " ", title) for title in titles]
label = "%s · %s" % (source_kicker(), source_name())
slides = []
for title in clean_titles:
slides.append(
render.Column(
children = [
render.Text(label, font = "CG-pixel-3x5-mono", color = "#fb7185"),
render.Marquee(
width = 64,
child = render.Text(title, color = "#f8fafc"),
offset_start = 2,
offset_end = 8,
),
],
),
)
return render.Root(
delay = 50,
child = render.Sequence(children = slides),
)
def search_sources(pattern):
options = [
schema.Option(display = "Pixel Wire", value = "pixel-wire"),
schema.Option(display = "City Desk", value = "city-desk"),
]
query = pattern.lower()
return [option for option in options if query in option.display.lower()]
def get_schema():
return schema.Schema(
version = "1",
fields = [
schema.Typeahead(
id = "source",
name = "News source",
desc = "Search for a news source.",
icon = "newspaper",
handler = search_sources,
),
],
)
How it works
xpathselects every RSS title, thenre.sub()normalizes whitespace.htmldemonstrates CSS-selector extraction;bsoupextracts the nested kicker.- Each title becomes a
Marquee, andSequenceadvances through the headlines. - The typeahead handler filters schema options as the user types.
- The live request has a 15-minute TTL to avoid repeatedly downloading the feed.
Connect a live feed
Replace news.example with an RSS URL and set DEMO_MODE to False. If the
feed uses a different structure, update the XPath passed to query_all().